feat(HAI-002): set status at engine processor lifecycle points

This commit is contained in:
Dustin Byrne
2026-03-25 19:24:11 -04:00
parent dc36ad1d57
commit ada968512e
4 changed files with 28 additions and 1 deletions

View File

@@ -73,6 +73,7 @@ export class TaskExecutor {
this.executing.add(task.id);
console.log(`[executor] Starting ${task.id}: ${task.title || task.id}`);
await this.store.updateTask(task.id, { status: "starting" });
try {
// Check dependencies
@@ -100,10 +101,13 @@ export class TaskExecutor {
this.options.onStart?.(task, worktreePath);
await this.store.updateTask(task.id, { status: "researching" });
// Read the task's PROMPT.md
const detail = await this.store.getTask(task.id);
// Create a pi agent session in the worktree
let hasStartedExecuting = false;
const { session } = await createHaiAgent({
cwd: worktreePath,
systemPrompt: EXECUTOR_SYSTEM_PROMPT,
@@ -111,6 +115,10 @@ export class TaskExecutor {
onText: (delta) => this.options.onAgentText?.(task.id, delta),
onToolStart: (name) => {
this.options.onAgentTool?.(task.id, name);
if (!hasStartedExecuting && /^(write|edit|bash)/i.test(name)) {
hasStartedExecuting = true;
this.store.updateTask(task.id, { status: "executing" }).catch(() => {});
}
},
});
@@ -128,14 +136,18 @@ export class TaskExecutor {
);
const doneCwd = join(worktreePath, ".DONE");
await this.store.updateTask(task.id, { status: "finalizing" });
if (existsSync(doneFile) || existsSync(doneCwd)) {
await this.store.moveTask(task.id, "in-review");
await this.store.updateTask(task.id, { status: "ready" });
console.log(`[executor] ✓ ${task.id} completed → in-review`);
this.options.onComplete?.(task);
} else {
// Agent finished but didn't create .DONE — still move to review
// so a human can inspect
await this.store.moveTask(task.id, "in-review");
await this.store.updateTask(task.id, { status: "ready" });
console.log(
`[executor] ⚠ ${task.id} agent finished without .DONE → in-review for inspection`,
);

View File

@@ -144,6 +144,8 @@ export async function aiMergeTask(
}
// 5. Spawn pi agent to resolve conflicts (if any) and write commit message
await store.updateTask(taskId, { status: "merging" });
console.log(
`[merger] ${taskId}: ${hasConflicts ? "resolving conflicts + " : ""}writing commit message`,
);

View File

@@ -78,6 +78,7 @@ export class Scheduler {
});
if (unmetDeps.length > 0) {
await this.store.updateTask(task.id, { status: "queued" });
this.options.onBlocked?.(task, unmetDeps);
continue;
}
@@ -87,10 +88,11 @@ export class Scheduler {
continue;
}
// Dependencies met — move to in-progress
// Dependencies met — clear status and move to in-progress
console.log(
`[scheduler] Starting ${task.id}: ${task.title || task.id} (deps satisfied)`,
);
await this.store.updateTask(task.id, { status: null });
await this.store.moveTask(task.id, "in-progress");
this.options.onSchedule?.(task);
started++;

View File

@@ -83,6 +83,13 @@ export class TriageProcessor {
(t) => t.column === "triage" && !this.processing.has(t.id),
);
for (const task of triageTasks) {
// Mark waiting tasks as queued
if (triageTasks.indexOf(task) > 0) {
await this.store.updateTask(task.id, { status: "queued" });
}
}
for (const task of triageTasks) {
// Process one at a time to avoid overwhelming the API
await this.specifyTask(task);
@@ -99,6 +106,7 @@ export class TriageProcessor {
console.log(`[triage] Specifying ${task.id}: ${task.title || task.id}`);
this.options.onSpecifyStart?.(task);
await this.store.updateTask(task.id, { status: "planning" });
try {
// Get the full task detail including current prompt
@@ -122,6 +130,9 @@ export class TriageProcessor {
// Run the agent
await session.prompt(agentPrompt);
// Clear status before moving to todo
await this.store.updateTask(task.id, { status: null });
// Move to todo
await this.store.moveTask(task.id, "todo");
console.log(`[triage] ✓ ${task.id} specified and moved to todo`);