fix: Phase completion check runs regardless of branch/merge status
handleTaskCompleted and handlePhaseAllTasksDone both bailed early when initiative had no branch, silently skipping phase status transitions. Also, merge failures would skip the phase completion check entirely. - Decouple phase completion check from branch existence - Wrap merge in try/catch so phase check runs even if merge fails - Route updateTaskStatus through dispatchManager.completeTask when completing, so the task:completed event fires for orchestration
This commit is contained in:
@@ -145,27 +145,29 @@ export class ExecutionOrchestrator {
|
||||
if (!task?.phaseId || !task.initiativeId) return;
|
||||
|
||||
const initiative = await this.initiativeRepository.findById(task.initiativeId);
|
||||
if (!initiative?.branch) return;
|
||||
|
||||
const phase = await this.phaseRepository.findById(task.phaseId);
|
||||
if (!phase) return;
|
||||
|
||||
// Skip merge for review/merge tasks — they already work on the phase branch directly
|
||||
if (task.category !== 'merge' && task.category !== 'review') {
|
||||
const initBranch = initiative.branch;
|
||||
const phBranch = phaseBranchName(initBranch, phase.name);
|
||||
const tBranch = taskBranchName(initBranch, task.id);
|
||||
// Merge task branch into phase branch (only when branches exist)
|
||||
if (initiative?.branch && task.category !== 'merge' && task.category !== 'review') {
|
||||
try {
|
||||
const initBranch = initiative.branch;
|
||||
const phBranch = phaseBranchName(initBranch, phase.name);
|
||||
const tBranch = taskBranchName(initBranch, task.id);
|
||||
|
||||
// Serialize merges per phase
|
||||
const lock = this.phaseMergeLocks.get(task.phaseId) ?? Promise.resolve();
|
||||
const mergeOp = lock.then(async () => {
|
||||
await this.mergeTaskIntoPhase(taskId, task.phaseId!, tBranch, phBranch);
|
||||
});
|
||||
this.phaseMergeLocks.set(task.phaseId, mergeOp.catch(() => {}));
|
||||
await mergeOp;
|
||||
// Serialize merges per phase
|
||||
const lock = this.phaseMergeLocks.get(task.phaseId) ?? Promise.resolve();
|
||||
const mergeOp = lock.then(async () => {
|
||||
await this.mergeTaskIntoPhase(taskId, task.phaseId!, tBranch, phBranch);
|
||||
});
|
||||
this.phaseMergeLocks.set(task.phaseId, mergeOp.catch(() => {}));
|
||||
await mergeOp;
|
||||
} catch (err) {
|
||||
log.error({ taskId, err: err instanceof Error ? err.message : String(err) }, 'task merge failed, still checking phase completion');
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all phase tasks are done
|
||||
// Check if all phase tasks are done — always, regardless of branch/merge status
|
||||
const phaseTasks = await this.taskRepository.findByPhaseId(task.phaseId);
|
||||
const allDone = phaseTasks.every((t) => t.status === 'completed');
|
||||
if (allDone) {
|
||||
@@ -233,10 +235,13 @@ export class ExecutionOrchestrator {
|
||||
if (!phase) return;
|
||||
|
||||
const initiative = await this.initiativeRepository.findById(phase.initiativeId);
|
||||
if (!initiative?.branch) return;
|
||||
if (!initiative) return;
|
||||
|
||||
if (initiative.executionMode === 'yolo') {
|
||||
await this.mergePhaseIntoInitiative(phaseId);
|
||||
// Merge phase branch into initiative branch (only when branches exist)
|
||||
if (initiative.branch) {
|
||||
await this.mergePhaseIntoInitiative(phaseId);
|
||||
}
|
||||
await this.phaseDispatchManager.completePhase(phaseId);
|
||||
|
||||
// Re-queue approved phases (self-healing: survives server restarts that wipe in-memory queue)
|
||||
|
||||
Reference in New Issue
Block a user