feat(HAI-055): add failed task status handling with retry support

- Set task status to 'failed' on execution failure in engine executor
- Add failed indicator styling on TaskCard component
- Add POST /tasks/:id/retry API endpoint and client function
- Add retry button in TaskDetailModal for failed tasks
- Add tests for failed indicator, retry endpoint, and modal behavior
This commit is contained in:
Dustin Byrne
2026-03-25 23:50:11 -04:00
parent 95ec4bc4a1
commit 35177f448f
13 changed files with 243 additions and 12 deletions

View File

@@ -143,6 +143,31 @@ describe("TaskExecutor with semaphore", () => {
expect(onError).toHaveBeenCalled();
});
it("sets task status to 'failed' when execution throws", async () => {
const store = createMockStore();
mockedCreateHaiAgent.mockRejectedValue(new Error("agent crashed"));
const onError = vi.fn();
const executor = new TaskExecutor(store, "/tmp/test", { onError });
await executor.execute({
id: "HAI-001",
title: "Test",
description: "Test",
column: "in-progress",
dependencies: [],
steps: [],
currentStep: 0,
log: [],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
expect(store.updateTask).toHaveBeenCalledWith("HAI-001", { status: "failed" });
expect(onError).toHaveBeenCalled();
});
it("concurrent executions respect semaphore limit", async () => {
const sem = new AgentSemaphore(1);
const store = createMockStore();

View File

@@ -351,6 +351,7 @@ export class TaskExecutor {
} catch (err: any) {
console.error(`[executor] ✗ ${task.id} execution failed:`, err.message);
await this.store.logEntry(task.id, `Execution failed: ${err.message}`);
await this.store.updateTask(task.id, { status: "failed" });
this.options.onError?.(task, err);
} finally {
this.executing.delete(task.id);