# All files / src/coordination conflict-resolution-service.ts

96% Statements 24/25
83.33% Branches 10/12
100% Functions 4/4
96% Lines 24/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159

    115x
    115x
    115x
    115x

    24x
    24x
    2x

    22x
    22x
    2x

    20x

    28x

    20x

    20x

    20x

    20x

    20x

    20x
    19x

    27x

    19x

    20x
    19x

    19x

     /**
     * 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<void>;
    }

    // =============================================================================
    // 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<void> {
        // 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}`,
          '',
        ];

        Iif (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);
        }
      }
    }