From 3624fd8d322fc192360c86a74e4af32354c3a316 Mon Sep 17 00:00:00 2001 From: NagyVikt Date: Thu, 23 Apr 2026 11:57:28 +0200 Subject: [PATCH] Hide temporary finish helpers from visible agent worktrees Finish helper worktrees were living under .omx/agent-worktrees and .omc/agent-worktrees, so VS Code surfaced __source-probe and __integrate repos beside real agent lanes. This moves those helpers into runtime-scoped .tmp-worktrees roots, deletes stale temporary refs at source and in prune sweeps, and extends scan-ignore/test coverage to keep the helper surface internal. Constraint: Parent workspace view intentionally exposes .omx/agent-worktrees and .omc/agent-worktrees as real SCM lanes Rejected: Keep helper worktrees under agent roots and only tweak labels | stale repos still surface in SCM and root scans Confidence: high Scope-risk: moderate Reversibility: clean Directive: Temporary finish helpers are internal-only; do not move them back under agent-worktrees without updating repo-scan ignores and prune behavior Tested: bash -n scripts/agent-branch-finish.sh scripts/agent-worktree-prune.sh templates/scripts/agent-branch-finish.sh templates/scripts/agent-worktree-prune.sh; node --test test/finish.test.js test/worktree.test.js test/setup.test.js test/metadata.test.js; openspec validate agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56 --type change --strict Not-tested: live GitHub PR finish flow on this manually created /tmp worktree lane --- .../proposal.md | 19 +++++++++ .../temp-helper-worktree-cleanup/spec.md | 41 +++++++++++++++++++ .../tasks.md | 26 ++++++++++++ scripts/agent-branch-finish.sh | 14 +++++-- scripts/agent-worktree-prune.sh | 23 ++++++++++- src/context.js | 4 ++ templates/scripts/agent-branch-finish.sh | 14 +++++-- templates/scripts/agent-worktree-prune.sh | 23 ++++++++++- test/finish.test.js | 4 +- test/helpers/install-test-helpers.js | 4 ++ test/setup.test.js | 4 ++ test/worktree.test.js | 24 ++++++++++- 12 files changed, 188 insertions(+), 12 deletions(-) create mode 100644 openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/proposal.md create mode 100644 openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/specs/temp-helper-worktree-cleanup/spec.md create mode 100644 openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/tasks.md diff --git a/openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/proposal.md b/openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/proposal.md new file mode 100644 index 00000000..240b8838 --- /dev/null +++ b/openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/proposal.md @@ -0,0 +1,19 @@ +# Proposal: fix temp helper worktree cleanup + +## Why + +- `gx branch finish` creates `__source-probe-*` and `__integrate-*` helper worktrees under `.omx/agent-worktrees` / `.omc/agent-worktrees`, so VS Code surfaces them beside real agent lanes. +- The finish flow removes temporary integration worktrees but can still leave stale `__agent_integrate_*` refs behind, which stacks up noisy helper branches after repeated doctor/finish runs. +- Repo-level Git scan ignores currently cover only durable agent worktree roots, so moving helper worktrees into a dedicated internal temp root also needs settings parity. + +## What changes + +- Move temporary finish helpers into `.omx/.tmp-worktrees` and `.omc/.tmp-worktrees` instead of the user-visible agent worktree roots. +- Delete temporary integration refs directly during finish cleanup and sweep any older stale temp refs in `gx cleanup`. +- Extend managed VS Code repo-scan ignores and focused regressions for the new temp-helper placement and stale-temp-branch cleanup. + +## Scope + +- Affected runtime/scripts: `scripts/agent-branch-finish.sh`, `scripts/agent-worktree-prune.sh` +- Affected template mirrors: `templates/scripts/*` +- Affected config/test surface: `src/context.js`, `test/finish.test.js`, `test/worktree.test.js`, `test/setup.test.js` diff --git a/openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/specs/temp-helper-worktree-cleanup/spec.md b/openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/specs/temp-helper-worktree-cleanup/spec.md new file mode 100644 index 00000000..b06b02c5 --- /dev/null +++ b/openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/specs/temp-helper-worktree-cleanup/spec.md @@ -0,0 +1,41 @@ +## ADDED Requirements + +### Requirement: finish helper worktrees stay outside durable agent roots + +`gx branch finish` SHALL create temporary `__source-probe-*` and `__integrate-*` helper worktrees under a runtime-scoped internal temp root (`.omx/.tmp-worktrees` for Codex lanes, `.omc/.tmp-worktrees` for Claude lanes) instead of the user-visible `.omx/agent-worktrees` or `.omc/agent-worktrees` roots. + +#### Scenario: Codex finish helper path stays outside `.omx/agent-worktrees` + +- **GIVEN** a Codex agent branch whose stored Guardex worktree root is `.omx/agent-worktrees` +- **WHEN** `gx branch finish` creates a temporary source-probe or integration helper worktree +- **THEN** the helper worktree path starts under `.omx/.tmp-worktrees` +- **AND** the helper worktree path does not start under `.omx/agent-worktrees` + +#### Scenario: Claude finish helper path stays outside `.omc/agent-worktrees` + +- **GIVEN** a Claude agent branch whose stored Guardex worktree root is `.omc/agent-worktrees` +- **WHEN** `gx branch finish` creates a temporary source-probe or integration helper worktree +- **THEN** the helper worktree path starts under `.omc/.tmp-worktrees` +- **AND** the helper worktree path does not start under `.omc/agent-worktrees` + +### Requirement: cleanup removes stale temporary helper refs + +`gx cleanup` and the finish exit path SHALL remove stale temporary helper refs (`__agent_integrate_*`, `__source-probe-*`) even when the matching helper worktree is already gone. + +#### Scenario: stale temporary integration ref is swept without a worktree + +- **GIVEN** a repo still has a local `__agent_integrate_*` branch ref +- **AND** no worktree is attached to that ref anymore +- **WHEN** `gx cleanup --delete-branches` runs +- **THEN** the stale temporary ref is deleted + +### Requirement: repo scan ignores cover internal temp helper roots + +Guardex-managed VS Code repo scan ignores SHALL include `.omx/.tmp-worktrees` and `.omc/.tmp-worktrees` alongside the durable agent worktree roots. + +#### Scenario: setup appends temp helper roots to repo scan ignores + +- **GIVEN** `.vscode/settings.json` already has user-defined `git.repositoryScanIgnoredFolders` +- **WHEN** `gx setup` or `gx doctor` refreshes the managed settings +- **THEN** the existing user-defined entries remain +- **AND** the resulting ignore list includes `.omx/.tmp-worktrees`, `**/.omx/.tmp-worktrees`, `.omc/.tmp-worktrees`, and `**/.omc/.tmp-worktrees` diff --git a/openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/tasks.md b/openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/tasks.md new file mode 100644 index 00000000..d859cbe3 --- /dev/null +++ b/openspec/changes/agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56/tasks.md @@ -0,0 +1,26 @@ +## 1. Spec + +- [x] 1.1 Define the helper-worktree placement and stale-temp-branch cleanup requirements. +- [x] 1.2 Capture the scope in `proposal.md`. + +## 2. Tests + +- [x] 2.1 Update finish/worktree regressions for `.tmp-worktrees` helper paths and stale temp-ref cleanup. +- [x] 2.2 Update setup expectations for the expanded repo-scan ignore list. + +## 3. Implementation + +- [x] 3.1 Move temporary finish helper worktrees into runtime-scoped `.tmp-worktrees` roots. +- [x] 3.2 Delete temporary integration refs at finish exit and sweep stale helper refs in `gx cleanup`. +- [x] 3.3 Extend repo scan ignore settings for temporary helper roots. + +## 4. Verification + +- [x] 4.1 Run focused tests for finish/prune/setup behavior. +- [x] 4.2 Run `openspec validate agent-codex-fix-temp-helper-worktree-cleanup-2026-04-23-11-56 --type change --strict`. + +## 5. Cleanup + +- [ ] 5.1 Run `gx branch finish --branch agent/codex/fix-temp-helper-worktree-cleanup-2026-04-23-11-56 --base main --via-pr --wait-for-merge --cleanup`. +- [ ] 5.2 Record PR URL + `MERGED` evidence. +- [ ] 5.3 Confirm sandbox worktree and temp refs are gone (`git worktree list`, `git branch -a`). diff --git a/scripts/agent-branch-finish.sh b/scripts/agent-branch-finish.sh index 456abaec..400a08c1 100755 --- a/scripts/agent-branch-finish.sh +++ b/scripts/agent-branch-finish.sh @@ -173,6 +173,8 @@ if [[ -z "$stored_worktree_root_rel" ]]; then stored_worktree_root_rel=".omx/agent-worktrees" fi agent_worktree_root="${repo_common_root}/${stored_worktree_root_rel}" +runtime_state_root_rel="$(dirname "$stored_worktree_root_rel")" +temp_worktree_root="${repo_common_root}/${runtime_state_root_rel}/.tmp-worktrees" if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -z "$BASE_BRANCH" ]]; then echo "[agent-branch-finish] --base requires a non-empty branch name." >&2 @@ -218,7 +220,7 @@ fi get_worktree_for_branch() { local branch="$1" - git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${agent_worktree_root}/__source-probe-" ' + git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${temp_worktree_root}/__source-probe-" ' $1 == "worktree" { wt = $2 } $1 == "branch" && $2 == target { if (index(wt, probe_prefix) != 1) { @@ -242,7 +244,7 @@ remove_stale_source_probe_worktrees() { git -C "$stale_probe" merge --abort >/dev/null 2>&1 || true git -C "$repo_root" worktree remove "$stale_probe" --force >/dev/null 2>&1 || true done < <( - git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${agent_worktree_root}/__source-probe-" ' + git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${temp_worktree_root}/__source-probe-" ' $1 == "worktree" { wt = $2 } $1 == "branch" && $2 == target { if (index(wt, probe_prefix) == 1) { @@ -264,11 +266,15 @@ source_worktree="$(get_worktree_for_branch "$SOURCE_BRANCH")" created_source_probe=0 source_probe_path="" integration_worktree="" +integration_branch="" cleanup() { if [[ -n "$integration_worktree" && -d "$integration_worktree" ]]; then git -C "$repo_root" worktree remove "$integration_worktree" --force >/dev/null 2>&1 || true fi + if [[ -n "${integration_branch:-}" ]]; then + git -C "$repo_root" branch -D "$integration_branch" >/dev/null 2>&1 || true + fi if [[ "$created_source_probe" -eq 1 && -n "$source_probe_path" && -d "$source_probe_path" ]]; then # Abort any in-progress git op so `worktree remove --force` succeeds on conflict-stuck probes. git -C "$source_probe_path" rebase --abort >/dev/null 2>&1 || true @@ -279,7 +285,7 @@ cleanup() { trap cleanup EXIT if [[ -z "$source_worktree" ]]; then - source_probe_path="${agent_worktree_root}/__source-probe-${SOURCE_BRANCH//\//__}-$(date +%Y%m%d-%H%M%S)" + source_probe_path="${temp_worktree_root}/__source-probe-${SOURCE_BRANCH//\//__}-$(date +%Y%m%d-%H%M%S)" mkdir -p "$(dirname "$source_probe_path")" git -C "$repo_root" worktree add "$source_probe_path" "$SOURCE_BRANCH" >/dev/null source_worktree="$source_probe_path" @@ -343,7 +349,7 @@ if [[ "$should_require_sync" -eq 1 ]] && git -C "$repo_root" show-ref --verify - fi integration_stamp="$(date +%Y%m%d-%H%M%S)" -integration_worktree_base="${agent_worktree_root}/__integrate-${BASE_BRANCH//\//__}-${integration_stamp}" +integration_worktree_base="${temp_worktree_root}/__integrate-${BASE_BRANCH//\//__}-${integration_stamp}" integration_branch_base="__agent_integrate_${BASE_BRANCH//\//_}_$(date +%Y%m%d_%H%M%S)" integration_worktree="$integration_worktree_base" integration_branch="$integration_branch_base" diff --git a/scripts/agent-worktree-prune.sh b/scripts/agent-worktree-prune.sh index 1145fdc8..f039a912 100755 --- a/scripts/agent-worktree-prune.sh +++ b/scripts/agent-worktree-prune.sh @@ -20,7 +20,9 @@ PR_MERGED_LOOKUP_LOADED=0 declare -A MERGED_PR_BRANCHES=() WORKTREE_ROOT_RELS=( ".omx/agent-worktrees" + ".omx/.tmp-worktrees" ".omc/agent-worktrees" + ".omc/.tmp-worktrees" ) if [[ -n "$BASE_BRANCH" ]]; then @@ -90,9 +92,15 @@ repo_common_dir="$(cd "$repo_common_dir" && pwd -P)" resolve_worktree_root_rel_for_entry() { local entry="$1" case "$entry" in + */.omc/.tmp-worktrees/*) + printf '%s' '.omc/.tmp-worktrees' + ;; */.omc/agent-worktrees/*) printf '%s' '.omc/agent-worktrees' ;; + */.omx/.tmp-worktrees/*) + printf '%s' '.omx/.tmp-worktrees' + ;; *) printf '%s' '.omx/agent-worktrees' ;; @@ -538,6 +546,19 @@ if [[ "$DELETE_BRANCHES" -eq 1 ]]; then if branch_has_worktree "$branch"; then continue fi + if [[ "$branch" == __agent_integrate_* || "$branch" == __source-probe-* ]]; then + if ! branch_idle_gate "$branch" "" "temporary-worktree"; then + continue + fi + if run_cmd git -C "$repo_root" branch -D "$branch" >/dev/null 2>&1; then + removed_branches=$((removed_branches + 1)) + echo "[agent-worktree-prune] Deleted stale temporary branch: ${branch}" + fi + continue + fi + if [[ "$branch" != agent/* ]]; then + continue + fi if ! branch_idle_gate "$branch" "" "stale-merged-branch"; then continue fi @@ -566,7 +587,7 @@ if [[ "$DELETE_BRANCHES" -eq 1 ]]; then fi fi fi - done < <(git -C "$repo_root" for-each-ref --format='%(refname:short)' refs/heads/agent) + done < <(git -C "$repo_root" for-each-ref --format='%(refname:short)' refs/heads) fi run_cmd git -C "$repo_root" worktree prune diff --git a/src/context.js b/src/context.js index cf2f21ce..2c931fec 100644 --- a/src/context.js +++ b/src/context.js @@ -263,8 +263,12 @@ const AGENT_WORKTREE_RELATIVE_DIRS = [ const MANAGED_REPO_SCAN_IGNORED_FOLDERS = [ '.omx/agent-worktrees', '**/.omx/agent-worktrees', + '.omx/.tmp-worktrees', + '**/.omx/.tmp-worktrees', '.omc/agent-worktrees', '**/.omc/agent-worktrees', + '.omc/.tmp-worktrees', + '**/.omc/.tmp-worktrees', ]; const MANAGED_GITIGNORE_PATHS = [ '.omx/', diff --git a/templates/scripts/agent-branch-finish.sh b/templates/scripts/agent-branch-finish.sh index 456abaec..400a08c1 100755 --- a/templates/scripts/agent-branch-finish.sh +++ b/templates/scripts/agent-branch-finish.sh @@ -173,6 +173,8 @@ if [[ -z "$stored_worktree_root_rel" ]]; then stored_worktree_root_rel=".omx/agent-worktrees" fi agent_worktree_root="${repo_common_root}/${stored_worktree_root_rel}" +runtime_state_root_rel="$(dirname "$stored_worktree_root_rel")" +temp_worktree_root="${repo_common_root}/${runtime_state_root_rel}/.tmp-worktrees" if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -z "$BASE_BRANCH" ]]; then echo "[agent-branch-finish] --base requires a non-empty branch name." >&2 @@ -218,7 +220,7 @@ fi get_worktree_for_branch() { local branch="$1" - git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${agent_worktree_root}/__source-probe-" ' + git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${temp_worktree_root}/__source-probe-" ' $1 == "worktree" { wt = $2 } $1 == "branch" && $2 == target { if (index(wt, probe_prefix) != 1) { @@ -242,7 +244,7 @@ remove_stale_source_probe_worktrees() { git -C "$stale_probe" merge --abort >/dev/null 2>&1 || true git -C "$repo_root" worktree remove "$stale_probe" --force >/dev/null 2>&1 || true done < <( - git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${agent_worktree_root}/__source-probe-" ' + git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${temp_worktree_root}/__source-probe-" ' $1 == "worktree" { wt = $2 } $1 == "branch" && $2 == target { if (index(wt, probe_prefix) == 1) { @@ -264,11 +266,15 @@ source_worktree="$(get_worktree_for_branch "$SOURCE_BRANCH")" created_source_probe=0 source_probe_path="" integration_worktree="" +integration_branch="" cleanup() { if [[ -n "$integration_worktree" && -d "$integration_worktree" ]]; then git -C "$repo_root" worktree remove "$integration_worktree" --force >/dev/null 2>&1 || true fi + if [[ -n "${integration_branch:-}" ]]; then + git -C "$repo_root" branch -D "$integration_branch" >/dev/null 2>&1 || true + fi if [[ "$created_source_probe" -eq 1 && -n "$source_probe_path" && -d "$source_probe_path" ]]; then # Abort any in-progress git op so `worktree remove --force` succeeds on conflict-stuck probes. git -C "$source_probe_path" rebase --abort >/dev/null 2>&1 || true @@ -279,7 +285,7 @@ cleanup() { trap cleanup EXIT if [[ -z "$source_worktree" ]]; then - source_probe_path="${agent_worktree_root}/__source-probe-${SOURCE_BRANCH//\//__}-$(date +%Y%m%d-%H%M%S)" + source_probe_path="${temp_worktree_root}/__source-probe-${SOURCE_BRANCH//\//__}-$(date +%Y%m%d-%H%M%S)" mkdir -p "$(dirname "$source_probe_path")" git -C "$repo_root" worktree add "$source_probe_path" "$SOURCE_BRANCH" >/dev/null source_worktree="$source_probe_path" @@ -343,7 +349,7 @@ if [[ "$should_require_sync" -eq 1 ]] && git -C "$repo_root" show-ref --verify - fi integration_stamp="$(date +%Y%m%d-%H%M%S)" -integration_worktree_base="${agent_worktree_root}/__integrate-${BASE_BRANCH//\//__}-${integration_stamp}" +integration_worktree_base="${temp_worktree_root}/__integrate-${BASE_BRANCH//\//__}-${integration_stamp}" integration_branch_base="__agent_integrate_${BASE_BRANCH//\//_}_$(date +%Y%m%d_%H%M%S)" integration_worktree="$integration_worktree_base" integration_branch="$integration_branch_base" diff --git a/templates/scripts/agent-worktree-prune.sh b/templates/scripts/agent-worktree-prune.sh index 1145fdc8..f039a912 100644 --- a/templates/scripts/agent-worktree-prune.sh +++ b/templates/scripts/agent-worktree-prune.sh @@ -20,7 +20,9 @@ PR_MERGED_LOOKUP_LOADED=0 declare -A MERGED_PR_BRANCHES=() WORKTREE_ROOT_RELS=( ".omx/agent-worktrees" + ".omx/.tmp-worktrees" ".omc/agent-worktrees" + ".omc/.tmp-worktrees" ) if [[ -n "$BASE_BRANCH" ]]; then @@ -90,9 +92,15 @@ repo_common_dir="$(cd "$repo_common_dir" && pwd -P)" resolve_worktree_root_rel_for_entry() { local entry="$1" case "$entry" in + */.omc/.tmp-worktrees/*) + printf '%s' '.omc/.tmp-worktrees' + ;; */.omc/agent-worktrees/*) printf '%s' '.omc/agent-worktrees' ;; + */.omx/.tmp-worktrees/*) + printf '%s' '.omx/.tmp-worktrees' + ;; *) printf '%s' '.omx/agent-worktrees' ;; @@ -538,6 +546,19 @@ if [[ "$DELETE_BRANCHES" -eq 1 ]]; then if branch_has_worktree "$branch"; then continue fi + if [[ "$branch" == __agent_integrate_* || "$branch" == __source-probe-* ]]; then + if ! branch_idle_gate "$branch" "" "temporary-worktree"; then + continue + fi + if run_cmd git -C "$repo_root" branch -D "$branch" >/dev/null 2>&1; then + removed_branches=$((removed_branches + 1)) + echo "[agent-worktree-prune] Deleted stale temporary branch: ${branch}" + fi + continue + fi + if [[ "$branch" != agent/* ]]; then + continue + fi if ! branch_idle_gate "$branch" "" "stale-merged-branch"; then continue fi @@ -566,7 +587,7 @@ if [[ "$DELETE_BRANCHES" -eq 1 ]]; then fi fi fi - done < <(git -C "$repo_root" for-each-ref --format='%(refname:short)' refs/heads/agent) + done < <(git -C "$repo_root" for-each-ref --format='%(refname:short)' refs/heads) fi run_cmd git -C "$repo_root" worktree prune diff --git a/test/finish.test.js b/test/finish.test.js index 07b39594..d40b3e63 100644 --- a/test/finish.test.js +++ b/test/finish.test.js @@ -224,7 +224,7 @@ test('agent-branch-finish removes stale source-probe worktrees before creating a const sourceProbePath = path.join( repoDir, '.omx', - 'agent-worktrees', + '.tmp-worktrees', '__source-probe-agent__test-stale-source-probe-20260422-153300', ); result = runCmd('git', ['worktree', 'add', sourceProbePath, 'agent/test-stale-source-probe'], repoDir); @@ -235,6 +235,8 @@ test('agent-branch-finish removes stale source-probe worktrees before creating a assert.equal(finish.status, 0, finish.stderr || finish.stdout); assert.match(finish.stderr, /Removing stale source-probe worktree for 'agent\/test-stale-source-probe'/); assert.equal(fs.existsSync(sourceProbePath), false, 'stale source-probe worktree should be removed before finish continues'); + result = runCmd('git', ['branch', '--list', '__agent_integrate_dev_*'], repoDir); + assert.equal(result.stdout.trim(), '', 'temporary integrate branches should be removed after finish exits'); assert.match( finish.stdout, /Merged 'agent\/test-stale-source-probe' into 'dev' via direct flow and kept source branch\/worktree\./, diff --git a/test/helpers/install-test-helpers.js b/test/helpers/install-test-helpers.js index e10db744..8280df24 100644 --- a/test/helpers/install-test-helpers.js +++ b/test/helpers/install-test-helpers.js @@ -157,8 +157,12 @@ function assertManagedRepoVscodeSettings(settings) { assert.deepEqual(settings['git.repositoryScanIgnoredFolders'], [ '.omx/agent-worktrees', '**/.omx/agent-worktrees', + '.omx/.tmp-worktrees', + '**/.omx/.tmp-worktrees', '.omc/agent-worktrees', '**/.omc/agent-worktrees', + '.omc/.tmp-worktrees', + '**/.omc/.tmp-worktrees', ]); } diff --git a/test/setup.test.js b/test/setup.test.js index bf951564..e3759bdf 100644 --- a/test/setup.test.js +++ b/test/setup.test.js @@ -1187,8 +1187,12 @@ test('setup merges Guardex repo-scan ignores into tracked VS Code workspace sett 'custom-folder', '.omx/agent-worktrees', '**/.omx/agent-worktrees', + '.omx/.tmp-worktrees', + '**/.omx/.tmp-worktrees', '.omc/agent-worktrees', '**/.omc/agent-worktrees', + '.omc/.tmp-worktrees', + '**/.omc/.tmp-worktrees', ]); }); diff --git a/test/worktree.test.js b/test/worktree.test.js index c91830ae..badc0a8f 100644 --- a/test/worktree.test.js +++ b/test/worktree.test.js @@ -151,7 +151,7 @@ test('worktree prune removes __source-probe worktrees even when they track agent const sourceProbePath = path.join( repoDir, '.omx', - 'agent-worktrees', + '.tmp-worktrees', '__source-probe-agent__test-source-probe-prune-20260422-153300', ); result = runCmd('git', ['worktree', 'add', sourceProbePath, 'agent/test-source-probe-prune'], repoDir); @@ -167,6 +167,28 @@ test('worktree prune removes __source-probe worktrees even when they track agent }); +test('worktree prune deletes stale temporary helper branches without worktrees', () => { + const repoDir = initRepo(); + let result = runNode(['setup', '--target', repoDir, '--no-global-install'], repoDir); + assert.equal(result.status, 0, result.stderr || result.stdout); + seedCommit(repoDir); + + result = runCmd('git', ['branch', '__agent_integrate_dev_20260423_114500', 'dev'], repoDir); + assert.equal(result.status, 0, result.stderr || result.stdout); + + result = runWorktreePrune(['--delete-branches'], repoDir); + assert.equal(result.status, 0, result.stderr || result.stdout); + assert.match(result.stdout, /Deleted stale temporary branch: __agent_integrate_dev_20260423_114500/); + + const branchResult = runCmd( + 'git', + ['show-ref', '--verify', '--quiet', 'refs/heads/__agent_integrate_dev_20260423_114500'], + repoDir, + ); + assert.notEqual(branchResult.status, 0, 'stale temporary helper branch should be removed'); +}); + + test('worktree prune reroutes foreign worktrees to the owning repo .omx root', () => { const repoDir = initRepo(); let result = runNode(['setup', '--target', repoDir, '--no-global-install'], repoDir);