/** * ConflictResolutionService * * Service responsible for handling merge conflicts by: * - Creating conflict resolution tasks * - Updating original task status * - Notifying agents via messages * - Emitting appropriate events * * This service is used by the CoordinationManager when merge conflicts occur. */ import type { EventBus, TaskQueuedEvent } from '../events/index.js'; import type { TaskRepository } from '../db/repositories/task-repository.js'; import type { AgentRepository } from '../db/repositories/agent-repository.js'; import type { MessageRepository } from '../db/repositories/message-repository.js'; // ============================================================================= // ConflictResolutionService Interface (Port) // ============================================================================= /** * Service interface for handling merge conflicts. * This is the PORT - implementations are ADAPTERS. */ /** * Branch context for merge conflicts from the branch hierarchy. */ export interface MergeContext { sourceBranch: string; targetBranch: string; } export interface ConflictResolutionService { /** * Handle a merge conflict by creating resolution task and notifying agent. * * @param taskId - ID of the task that conflicted * @param conflicts - List of conflicting file paths * @param mergeContext - Optional branch context for branch hierarchy merges */ handleConflict(taskId: string, conflicts: string[], mergeContext?: MergeContext): Promise; } // ============================================================================= // DefaultConflictResolutionService Implementation (Adapter) // ============================================================================= /** * Default implementation of ConflictResolutionService. * * Creates conflict resolution tasks, updates task statuses, sends messages * to agents, and emits events when merge conflicts occur. */ export class DefaultConflictResolutionService implements ConflictResolutionService { constructor( private taskRepository: TaskRepository, private agentRepository: AgentRepository, private messageRepository?: MessageRepository, private eventBus?: EventBus ) {} /** * Handle a merge conflict. * Creates a conflict-resolution task and notifies the agent via message. */ async handleConflict(taskId: string, conflicts: string[], mergeContext?: MergeContext): Promise { // Get original task for context const originalTask = await this.taskRepository.findById(taskId); if (!originalTask) { throw new Error(`Original task not found: ${taskId}`); } // Get agent that was working on the task const agent = await this.agentRepository.findByTaskId(taskId); if (!agent) { throw new Error(`No agent found for task: ${taskId}`); } // Build conflict description const descriptionLines = [ 'Merge conflicts detected. Resolve conflicts in the following files:', '', ...conflicts.map((f) => `- ${f}`), '', `Original task: ${originalTask.name}`, '', ]; if (mergeContext) { descriptionLines.push( `Resolve merge conflicts between branch "${mergeContext.sourceBranch}" and "${mergeContext.targetBranch}".`, `Run: git merge ${mergeContext.sourceBranch} --no-edit`, 'Resolve all conflicts, then: git add . && git commit', ); } else { descriptionLines.push( 'Instructions: Resolve merge conflicts in the listed files, then mark task complete.', ); } const conflictDescription = descriptionLines.join('\n'); // Create new conflict-resolution task const conflictTask = await this.taskRepository.create({ parentTaskId: originalTask.parentTaskId, phaseId: originalTask.phaseId, initiativeId: originalTask.initiativeId, name: `Resolve conflicts: ${originalTask.name}`, description: conflictDescription, category: mergeContext ? 'merge' : 'execute', type: 'auto', priority: 'high', status: 'pending', order: originalTask.order + 1, }); // Update original task status to blocked await this.taskRepository.update(taskId, { status: 'blocked' }); // Create message to agent if messageRepository is configured if (this.messageRepository) { const messageContent = [ `Merge conflict detected for task: ${originalTask.name}`, '', 'Conflicting files:', ...conflicts.map((f) => `- ${f}`), '', `A new task has been created to resolve these conflicts: ${conflictTask.name}`, '', 'Please resolve the merge conflicts in the listed files and mark the resolution task as complete.', ].join('\n'); await this.messageRepository.create({ senderType: 'user', // System-generated messages appear as from user senderId: null, recipientType: 'agent', recipientId: agent.id, type: 'info', content: messageContent, requiresResponse: false, }); } // Emit TaskQueuedEvent for the new conflict-resolution task if (this.eventBus) { const event: TaskQueuedEvent = { type: 'task:queued', timestamp: new Date(), payload: { taskId: conflictTask.id, priority: 'high', dependsOn: [], }, }; this.eventBus.emit(event); } } }