Enhance task status handling to allow 'done' status in limbo state. Added worktree existence check to enforce merge workflow unless no worktree is found, enabling direct status updates in specific scenarios.

This commit is contained in:
AndyMik90
2025-12-18 16:24:40 +01:00
parent 0ed6afb805
commit a20b8cf12a
@@ -238,23 +238,33 @@ export function registerTaskExecutionHandlers(
taskId: string, taskId: string,
status: TaskStatus status: TaskStatus
): Promise<IPCResult> => { ): Promise<IPCResult> => {
// Validate status transition - 'done' can only be set through merge handler // Find task and project first (needed for worktree check)
// This prevents AI agents from bypassing the human review workflow
if (status === 'done') {
console.warn(`[TASK_UPDATE_STATUS] Blocked attempt to set status 'done' directly for task ${taskId}. Use merge workflow instead.`);
return {
success: false,
error: "Cannot set status to 'done' directly. Complete the human review and merge the worktree changes instead."
};
}
// Find task and project
const { task, project } = findTaskAndProject(taskId); const { task, project } = findTaskAndProject(taskId);
if (!task || !project) { if (!task || !project) {
return { success: false, error: 'Task not found' }; return { success: false, error: 'Task not found' };
} }
// Validate status transition - 'done' can only be set through merge handler
// UNLESS there's no worktree (limbo state - already merged/discarded or failed)
if (status === 'done') {
// Check if worktree exists
const worktreePath = path.join(project.path, '.worktrees', taskId);
const hasWorktree = existsSync(worktreePath);
if (hasWorktree) {
// Worktree exists - must use merge workflow
console.warn(`[TASK_UPDATE_STATUS] Blocked attempt to set status 'done' directly for task ${taskId}. Use merge workflow instead.`);
return {
success: false,
error: "Cannot set status to 'done' directly. Complete the human review and merge the worktree changes instead."
};
} else {
// No worktree - allow marking as done (limbo state recovery)
console.log(`[TASK_UPDATE_STATUS] Allowing status 'done' for task ${taskId} (no worktree found - limbo state)`);
}
}
// Get the spec directory // Get the spec directory
const specsBaseDir = getSpecsDir(project.autoBuildPath); const specsBaseDir = getSpecsDir(project.autoBuildPath);
const specDir = path.join( const specDir = path.join(
@@ -274,17 +284,16 @@ export function registerTaskExecutionHandlers(
// Store the exact UI status - project-store.ts will map it back // Store the exact UI status - project-store.ts will map it back
plan.status = status; plan.status = status;
// Also store mapped version for Python compatibility // Also store mapped version for Python compatibility
// Note: 'done' is blocked at the start of this handler - only set via merge workflow
plan.planStatus = status === 'in_progress' ? 'in_progress' plan.planStatus = status === 'in_progress' ? 'in_progress'
: status === 'ai_review' ? 'review' : status === 'ai_review' ? 'review'
: status === 'human_review' ? 'review' : status === 'human_review' ? 'review'
: status === 'done' ? 'completed'
: 'pending'; : 'pending';
plan.updated_at = new Date().toISOString(); plan.updated_at = new Date().toISOString();
writeFileSync(planPath, JSON.stringify(plan, null, 2)); writeFileSync(planPath, JSON.stringify(plan, null, 2));
} else { } else {
// If no implementation plan exists yet, create a basic one // If no implementation plan exists yet, create a basic one
// Note: 'done' status is blocked at the start of this handler
const plan = { const plan = {
feature: task.title, feature: task.title,
description: task.description || '', description: task.description || '',
@@ -294,6 +303,7 @@ export function registerTaskExecutionHandlers(
planStatus: status === 'in_progress' ? 'in_progress' planStatus: status === 'in_progress' ? 'in_progress'
: status === 'ai_review' ? 'review' : status === 'ai_review' ? 'review'
: status === 'human_review' ? 'review' : status === 'human_review' ? 'review'
: status === 'done' ? 'completed'
: 'pending', : 'pending',
phases: [] phases: []
}; };