fix(engine): prevent phantom merges when verification fix runs without a commit
When the AI merge agent reported a build failure via fn_report_build_failure, executeMergeAttempt immediately ran `git reset --merge` and threw. The catch handler then spawned an in-merge fix agent on a clean main and called amendMergeCommitWithFixes, which blindly amended HEAD — the *previous* task's merge commit — silently dropping the current task's branch and inheriting the prior task's stats. The dashboard then reported the new task as merged with completely unrelated files. - Drop the immediate reset at the build-failure throw site so the squash state survives for the in-merge fix path. - Capture preAttemptHeadSha at each mergeAttempt and refuse to amend when HEAD never moved past it; instead, create a fresh commit from the squash + fix changes. If neither HEAD moved nor anything is staged, abort the merge instead of fabricating success. - Move the cleanup reset into the mergeAttempt catch handler (with a labeled resetMergeWithWarn helper) so it still fires when the fix path is exhausted or disabled. - Replace the AI-authored commit body with a deterministic body built from the branch's actual step-commit subjects after every successful AI merge. Stops the recurring problem of merge messages describing files that are not in the diff. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2964,7 +2964,11 @@ describe("aiMergeTask — reset cleanup failure diagnostics", () => {
|
||||
.map(([message]) => String(message))
|
||||
.filter((message) => message.includes("git reset --merge cleanup failed"));
|
||||
|
||||
expect(cleanupWarnMessages.some((message) => message.includes("build-verification reset"))).toBe(true);
|
||||
// Reset cleanup now runs from mergeAttempt's catch handler (after the
|
||||
// squash state is preserved across the build-failure throw site for the
|
||||
// in-merge fix path). With both verificationFixRetries=0 and
|
||||
// buildRetryCount=0 the rollback fires from the "no retries left" branch.
|
||||
expect(cleanupWarnMessages.some((message) => message.includes("build-verification rollback"))).toBe(true);
|
||||
expect(cleanupWarnMessages.some((message) => message.includes(resetFailureMessage))).toBe(true);
|
||||
|
||||
warnSpy.mockRestore();
|
||||
|
||||
Reference in New Issue
Block a user