package repository import ( "sort" "strings" "time" ) // DefaultTaskMatcher implements TaskMatcher interface type DefaultTaskMatcher struct{} // MatchTasksToRole filters tasks suitable for a specific role and expertise func (m *DefaultTaskMatcher) MatchTasksToRole(tasks []*Task, role string, expertise []string) ([]*Task, error) { var matchedTasks []*Task for _, task := range tasks { score := m.ScoreTaskForAgent(task, role, expertise) if score > 0.3 { // Minimum threshold for task suitability matchedTasks = append(matchedTasks, task) } } // Sort by score (highest first) sort.Slice(matchedTasks, func(i, j int) bool { scoreI := m.ScoreTaskForAgent(matchedTasks[i], role, expertise) scoreJ := m.ScoreTaskForAgent(matchedTasks[j], role, expertise) return scoreI > scoreJ }) return matchedTasks, nil } // ScoreTaskForAgent calculates how suitable a task is for an agent based on role and expertise func (m *DefaultTaskMatcher) ScoreTaskForAgent(task *Task, agentRole string, agentExpertise []string) float64 { score := 0.0 // Base score for role matching if task.RequiredRole != "" { if task.RequiredRole == agentRole { score += 0.5 // Perfect role match } else if m.isCompatibleRole(task.RequiredRole, agentRole) { score += 0.3 // Compatible role } } else { // No specific role required, default bonus for general roles if m.isGeneralRole(agentRole) { score += 0.2 } } // Expertise matching expertiseScore := m.calculateExpertiseScore(task.RequiredExpertise, agentExpertise) score += expertiseScore * 0.4 // Priority bonus (higher priority tasks get small bonus) priorityBonus := float64(task.Priority) / 10.0 * 0.1 score += priorityBonus // Task type bonuses based on agent role taskTypeScore := m.calculateTaskTypeScore(task, agentRole) score += taskTypeScore * 0.1 // Label-based scoring labelScore := m.calculateLabelScore(task.Labels, agentRole, agentExpertise) score += labelScore * 0.1 // Ensure score is between 0 and 1 if score > 1.0 { score = 1.0 } if score < 0.0 { score = 0.0 } return score } // GetRecommendedAgents returns agents best suited for a task func (m *DefaultTaskMatcher) GetRecommendedAgents(task *Task, availableAgents []AgentInfo) ([]AgentInfo, error) { type agentScore struct { agent AgentInfo score float64 } var scoredAgents []agentScore for _, agent := range availableAgents { // Skip agents that are offline or at capacity if agent.Status != "online" && agent.Status != "ready" { continue } if agent.CurrentTasks >= agent.MaxTasks { continue } // Calculate base task suitability score taskScore := m.ScoreTaskForAgent(task, agent.Role, agent.Expertise) // Apply agent-specific modifiers finalScore := taskScore // Performance modifier (0.5 to 1.5 multiplier) performanceMod := 0.5 + agent.Performance finalScore *= performanceMod // Availability modifier availabilityMod := agent.Availability finalScore *= availabilityMod // Workload penalty (agents with fewer current tasks get slight bonus) workloadRatio := float64(agent.CurrentTasks) / float64(agent.MaxTasks) workloadBonus := (1.0 - workloadRatio) * 0.1 finalScore += workloadBonus // Recent activity bonus (agents seen recently get small bonus) timeSinceLastSeen := time.Since(agent.LastSeen) if timeSinceLastSeen < 5*time.Minute { finalScore += 0.05 } if finalScore > 0.1 { // Minimum threshold scoredAgents = append(scoredAgents, agentScore{agent: agent, score: finalScore}) } } // Sort by score (highest first) sort.Slice(scoredAgents, func(i, j int) bool { return scoredAgents[i].score > scoredAgents[j].score }) // Return top agents (up to 5) maxAgents := 5 if len(scoredAgents) < maxAgents { maxAgents = len(scoredAgents) } result := make([]AgentInfo, maxAgents) for i := 0; i < maxAgents; i++ { result[i] = scoredAgents[i].agent } return result, nil } // calculateExpertiseScore computes overlap between required and agent expertise func (m *DefaultTaskMatcher) calculateExpertiseScore(requiredExpertise, agentExpertise []string) float64 { if len(requiredExpertise) == 0 { return 0.5 // No specific expertise required } matches := 0 for _, required := range requiredExpertise { for _, agent := range agentExpertise { if strings.EqualFold(required, agent) || m.isRelatedExpertise(required, agent) { matches++ break } } } // Score based on percentage of required expertise covered score := float64(matches) / float64(len(requiredExpertise)) // Bonus for having additional relevant expertise if len(agentExpertise) > len(requiredExpertise) { bonus := 0.1 * float64(len(agentExpertise)-len(requiredExpertise)) / float64(len(agentExpertise)) score += bonus } return score } // calculateTaskTypeScore gives bonuses based on task type and agent role compatibility func (m *DefaultTaskMatcher) calculateTaskTypeScore(task *Task, agentRole string) float64 { taskType := strings.ToLower(task.TaskType) role := strings.ToLower(agentRole) switch taskType { case "bug_fix", "bug": if strings.Contains(role, "qa") || strings.Contains(role, "test") { return 0.8 } if strings.Contains(role, "backend") || strings.Contains(role, "full_stack") { return 0.6 } case "feature", "enhancement": if strings.Contains(role, "developer") || strings.Contains(role, "engineer") { return 0.7 } case "documentation", "docs": if strings.Contains(role, "writer") || strings.Contains(role, "documentation") { return 0.9 } case "security": if strings.Contains(role, "security") { return 0.9 } case "design": if strings.Contains(role, "designer") || strings.Contains(role, "ux") { return 0.9 } case "infrastructure", "devops": if strings.Contains(role, "devops") || strings.Contains(role, "systems") { return 0.9 } } return 0.0 } // calculateLabelScore analyzes task labels for additional role matching func (m *DefaultTaskMatcher) calculateLabelScore(labels []string, agentRole string, agentExpertise []string) float64 { score := 0.0 role := strings.ToLower(agentRole) for _, label := range labels { label = strings.ToLower(label) // Direct role matches if strings.Contains(role, label) || strings.Contains(label, role) { score += 0.3 } // Expertise matches for _, expertise := range agentExpertise { if strings.Contains(strings.ToLower(expertise), label) || strings.Contains(label, strings.ToLower(expertise)) { score += 0.2 } } // Specific label bonuses switch label { case "frontend": if strings.Contains(role, "frontend") || strings.Contains(role, "ui") { score += 0.4 } case "backend": if strings.Contains(role, "backend") || strings.Contains(role, "api") { score += 0.4 } case "urgent", "critical": score += 0.1 // Small bonus for urgent tasks case "good first issue", "beginner": if strings.Contains(role, "junior") { score += 0.3 } } } return score } // isCompatibleRole checks if two roles are compatible func (m *DefaultTaskMatcher) isCompatibleRole(requiredRole, agentRole string) bool { compatibilityMap := map[string][]string{ "frontend_developer": {"full_stack_engineer", "ui_ux_designer"}, "backend_developer": {"full_stack_engineer", "database_engineer"}, "full_stack_engineer": {"frontend_developer", "backend_developer"}, "qa_engineer": {"full_stack_engineer", "backend_developer"}, "devops_engineer": {"systems_engineer", "backend_developer"}, "ui_ux_designer": {"frontend_developer", "lead_designer"}, "security_expert": {"backend_developer", "senior_software_architect"}, "technical_writer": {"full_stack_engineer"}, "database_engineer": {"backend_developer", "full_stack_engineer"}, "senior_software_architect": {"full_stack_engineer", "backend_developer", "frontend_developer"}, } compatibleRoles, exists := compatibilityMap[requiredRole] if !exists { return false } for _, compatible := range compatibleRoles { if compatible == agentRole { return true } } return false } // isGeneralRole checks if a role is considered general-purpose func (m *DefaultTaskMatcher) isGeneralRole(role string) bool { generalRoles := []string{ "full_stack_engineer", "senior_software_architect", "general_developer", } for _, general := range generalRoles { if general == role { return true } } return false } // isRelatedExpertise checks if two expertise areas are related func (m *DefaultTaskMatcher) isRelatedExpertise(required, agent string) bool { relatedMap := map[string][]string{ "frontend": {"javascript", "typescript", "react", "vue", "angular", "css", "html"}, "backend": {"api_development", "server_frameworks", "databases", "microservices"}, "database": {"sql", "nosql", "data_modeling", "query_optimization"}, "security": {"cybersecurity", "owasp", "vulnerability_analysis", "penetration_testing"}, "devops": {"deployment", "infrastructure", "docker", "kubernetes", "cicd"}, "testing": {"qa_methodologies", "test_automation", "debugging"}, "design": {"ui_ux", "user_experience", "prototyping", "design_systems"}, "documentation": {"technical_writing", "api_documentation"}, } required = strings.ToLower(required) agent = strings.ToLower(agent) // Check direct related expertise if related, exists := relatedMap[required]; exists { for _, rel := range related { if strings.Contains(agent, rel) || strings.Contains(rel, agent) { return true } } } // Check reverse mapping if related, exists := relatedMap[agent]; exists { for _, rel := range related { if strings.Contains(required, rel) || strings.Contains(rel, required) { return true } } } return false }