diff --git a/src/executor.ts b/src/executor.ts index f148560..5805440 100644 --- a/src/executor.ts +++ b/src/executor.ts @@ -19,10 +19,7 @@ import { buildConflictResolutionPrompt, MAX_DIFF_BYTES, } from "./prompts"; -import { - compileIgnorePatterns, - type DiffOptions, -} from "./diff"; +import { compileIgnorePatterns } from "./diff"; import { extractReflection } from "./reflection"; import { extractReview, @@ -49,6 +46,7 @@ import { ensureDir, captureGitCommits, captureGitHead, + canComputeRange, getCommitRangeDiff, hasUncommittedChanges, getGitStatusPorcelain, @@ -918,6 +916,15 @@ async function executeTask( ); break; } + // Cheap guard mirroring canCompareToBase: if the captured base ref no + // longer resolves (stale/broken worktree ref), warn explicitly and + // never treat the task as review-verified. + if (!canComputeRange(worktreeDir, baseRef)) { + sendChatMessage?.( + `~ review for ${task.id} · ${task.title} — diff could not be computed (base ref ${baseRef} no longer resolves)`, + ); + break; + } const rangeDiff = getCommitRangeDiff(worktreeDir, baseRef); if (rangeDiff.kind === "error") { sendChatMessage?.( diff --git a/src/utils.ts b/src/utils.ts index 06ca176..275670f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -930,7 +930,10 @@ export function canComputeRange(projectDir: string, baseRef: string): boolean { const { execSync } = require("node:child_process"); if (!/^[0-9a-f]{7,40}$/i.test(baseRef)) return false; try { - execSync(`git rev-parse --verify ${baseRef}`, { + // git cat-file -e truly verifies the object EXISTS (rev-parse --verify + // accepts any 40-hex SHA even if it was never created), so a stale/broken + // base ref is caught here rather than silently treated as no-changes. + execSync(`git cat-file -e ${baseRef}`, { cwd: projectDir, stdio: "pipe", }); @@ -961,9 +964,11 @@ export function getCommitRangeDiff( } // Verify the base ref resolves before diffing — a stale/unfetched ref is a - // computation failure, not a clean "no changes" signal. + // computation failure, not a clean "no changes" signal. git cat-file -e + // checks the object genuinely exists (rev-parse --verify would accept any + // 40-hex SHA even if it was never created). try { - execSync(`git rev-parse --verify ${baseRef}`, { + execSync(`git cat-file -e ${baseRef}`, { cwd: projectDir, stdio: "pipe", });