fix: improve merge verification and dashboard behavior
This commit is contained in:
@@ -2152,6 +2152,57 @@ describe("aiMergeTask — deterministic merge verification", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("does not fail verification when verbose test output exceeds buffer after exit 0", async () => {
|
||||
mockedExecSync.mockImplementation((cmd: any) => {
|
||||
const cmdStr = String(cmd);
|
||||
if (cmdStr.includes("rev-parse --verify")) return Buffer.from("abc123");
|
||||
if (cmdStr.includes("git log")) return "- feat: something" as any;
|
||||
if (cmdStr.includes("merge-base")) return Buffer.from("abc123");
|
||||
if (cmdStr.includes("git diff") && cmdStr.includes("--stat")) return "1 file changed" as any;
|
||||
if (cmdStr.includes("merge --squash")) return Buffer.from("");
|
||||
if (cmdStr.includes("vitest run")) {
|
||||
const error = new Error("stdout maxBuffer length exceeded") as any;
|
||||
error.code = "ENOBUFS";
|
||||
error.status = 0;
|
||||
error.stdout = "tests passed but output was verbose";
|
||||
error.stderr = "";
|
||||
throw error;
|
||||
}
|
||||
if (cmdStr.includes("diff --cached --quiet")) return "1" as any;
|
||||
if (cmdStr.includes("diff --cached") && !cmdStr.includes("--quiet")) return "" as any;
|
||||
if (cmdStr.includes("branch -d") || cmdStr.includes("branch -D")) return Buffer.from("");
|
||||
if (cmdStr.includes("worktree remove")) return Buffer.from("");
|
||||
if (cmdStr === "git rev-parse HEAD" || cmdStr.startsWith("git rev-parse HEAD ")) return "mergedcommit123";
|
||||
if (cmdStr.includes("show --shortstat")) return "1 file changed, 1 insertion(+)" as any;
|
||||
return Buffer.from("");
|
||||
});
|
||||
|
||||
mockedCreateHaiAgent.mockImplementation(async () => ({
|
||||
session: {
|
||||
prompt: vi.fn().mockResolvedValue(undefined),
|
||||
dispose: vi.fn(),
|
||||
},
|
||||
}) as any);
|
||||
|
||||
const store = createMockStore(
|
||||
{ id: "FN-050", worktree: "/tmp/root/.worktrees/KB-050" },
|
||||
[{ id: "FN-050", worktree: "/tmp/root/.worktrees/KB-050", column: "in-review" } as Task],
|
||||
);
|
||||
(store.getSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
...DEFAULT_SETTINGS,
|
||||
testCommand: "vitest run",
|
||||
});
|
||||
|
||||
const result = await aiMergeTask(store, "/tmp/root", "FN-050");
|
||||
|
||||
expect(result.merged).toBe(true);
|
||||
expect(store.moveTask).toHaveBeenCalledWith("FN-050", "done");
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-050",
|
||||
"[verification] test command succeeded (exit 0, output exceeded buffer)",
|
||||
);
|
||||
});
|
||||
|
||||
it("fails merge when buildCommand fails and does not move task to done", async () => {
|
||||
// Setup exec mock that will be updated after agent commits
|
||||
mockedExecSync.mockImplementation((cmd: any) => {
|
||||
|
||||
@@ -63,6 +63,14 @@ const DEPENDENCY_SYNC_TRIGGER_PATTERNS = [
|
||||
"packages/*/package.json",
|
||||
];
|
||||
|
||||
const VERIFICATION_COMMAND_MAX_BUFFER = 50 * 1024 * 1024;
|
||||
const VERIFICATION_LOG_MAX_CHARS = 20_000;
|
||||
|
||||
function truncateVerificationOutput(output: string): string {
|
||||
if (output.length <= VERIFICATION_LOG_MAX_CHARS) return output;
|
||||
return `... output truncated to last ${VERIFICATION_LOG_MAX_CHARS} characters ...\n${output.slice(-VERIFICATION_LOG_MAX_CHARS)}`;
|
||||
}
|
||||
|
||||
/** Check if a path matches a glob pattern (simple glob support: * and **) */
|
||||
function matchGlob(path: string, pattern: string): boolean {
|
||||
// Handle ** which matches across directory boundaries (must do before single *)
|
||||
@@ -306,6 +314,7 @@ async function runVerificationCommand(
|
||||
const output = execSync(command, {
|
||||
cwd: rootDir,
|
||||
encoding: "utf-8",
|
||||
maxBuffer: VERIFICATION_COMMAND_MAX_BUFFER,
|
||||
timeout: 300_000, // 5 minute timeout for verification commands
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
});
|
||||
@@ -318,10 +327,22 @@ async function runVerificationCommand(
|
||||
result.stdout = error.stdout?.toString() || "";
|
||||
result.stderr = error.stderr?.toString() || "";
|
||||
result.exitCode = error.status ?? null;
|
||||
result.success = false;
|
||||
const maxBufferExceeded = error.code === "ENOBUFS"
|
||||
|| error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER"
|
||||
|| error.message?.includes("maxBuffer");
|
||||
result.success = maxBufferExceeded && result.exitCode === 0;
|
||||
|
||||
if (result.success) {
|
||||
mergerLog.log(`${taskId}: ${type} command succeeded (exit 0, output exceeded buffer)`);
|
||||
await store.logEntry(
|
||||
taskId,
|
||||
`[verification] ${type} command succeeded (exit 0, output exceeded buffer)`,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Build a useful error summary
|
||||
const summary = result.stderr || result.stdout || error.message || "Unknown error";
|
||||
const summary = truncateVerificationOutput(result.stderr || result.stdout || error.message || "Unknown error");
|
||||
mergerLog.error(`${taskId}: ${type} command failed (exit ${result.exitCode}): ${summary.trim()}`);
|
||||
await store.logEntry(
|
||||
taskId,
|
||||
|
||||
Reference in New Issue
Block a user