diff --git a/packages/core/src/evaluation/results-repo.ts b/packages/core/src/evaluation/results-repo.ts index e57c9cb6b..e86ba98db 100644 --- a/packages/core/src/evaluation/results-repo.ts +++ b/packages/core/src/evaluation/results-repo.ts @@ -4124,7 +4124,19 @@ export async function deleteWipBranch(params: { }): Promise { const normalized = normalizeResultsConfig(params.config); const cloneDir = await ensureResultsRepoClone(normalized); - await runGit(['push', normalized.remote, '--delete', params.wipBranch], { cwd: cloneDir }); + const result = await runGit(['push', normalized.remote, '--delete', params.wipBranch], { + cwd: cloneDir, + check: false, + }); + if (result.exitCode === 0 || isMissingRemoteRefDelete(result)) { + return; + } + throw new Error(result.stderr.trim() || result.stdout.trim() || 'Failed to delete WIP branch'); +} + +function isMissingRemoteRefDelete(result: { stdout: string; stderr: string }): boolean { + const text = `${result.stderr}\n${result.stdout}`.toLowerCase(); + return text.includes('remote ref does not exist'); } // git exits non-zero with one of these messages when the requested ref/object diff --git a/packages/core/test/evaluation/results-repo.test.ts b/packages/core/test/evaluation/results-repo.test.ts index 408546090..651df142d 100644 --- a/packages/core/test/evaluation/results-repo.test.ts +++ b/packages/core/test/evaluation/results-repo.test.ts @@ -2808,4 +2808,46 @@ describe('WIP branch helpers', () => { const branchesAfter = git('git branch -r', cloneDir); expect(branchesAfter).not.toContain(`origin/${wipBranch}`); }, 30000); + + it('deleteWipBranch ignores a missing remote WIP branch', async () => { + await expect( + deleteWipBranch({ config, wipBranch: 'agentv/wip/test-host/missing-run' }), + ).resolves.toBeUndefined(); + }, 30000); + + it('deleteWipBranch surfaces non-missing remote delete failures', async () => { + const wipBranch = 'agentv/wip/test-host/rejected-delete'; + const handle = await setupWipWorktree({ config, wipBranch }); + const runDir = path.join(rootDir, 'run-rejected-delete-test'); + writeRunArtifacts(runDir, 'default', '2026-01-15T13-00-00'); + + try { + await pushWipCheckpoint({ + handle, + sourceDir: runDir, + destinationPath: 'default/2026-01-15T13-00-00', + }); + } finally { + await handle.cleanup(); + } + + const hookPath = path.join(remoteDir, 'hooks', 'update'); + writeFileSync( + hookPath, + [ + '#!/usr/bin/env sh', + 'if [ "$3" = "0000000000000000000000000000000000000000" ]; then', + ' echo "unable to delete protected WIP branch" >&2', + ' exit 1', + 'fi', + 'exit 0', + '', + ].join('\n'), + ); + chmodSync(hookPath, 0o755); + + await expect(deleteWipBranch({ config, wipBranch })).rejects.toThrow( + /unable to delete protected WIP branch|hook declined/, + ); + }, 30000); });