UNPKG

@versatil/sdlc-framework

Version:

🚀 AI-Native SDLC framework with 11-MCP ecosystem, RAG memory, OPERA orchestration, and 6 specialized agents achieving ZERO CONTEXT LOSS. Features complete CI/CD pipeline with 7 GitHub workflows (MCP testing, security scanning, performance benchmarking),

879 lines (733 loc) • 27.1 kB
/** * VERSATIL SDLC Framework - Parallel Task Manager * Implements Rule 1: Run many tasks in parallel if not colliding with SDLC process * * Features: * - Intelligent collision detection * - SDLC process-aware task orchestration * - Resource contention prevention * - Agent coordination and handoff management */ import { EventEmitter } from 'events'; import { EnvironmentManager } from '../environment/environment-manager.js'; export interface Task { id: string; name: string; type: TaskType; priority: Priority; estimatedDuration: number; requiredResources: Resource[]; dependencies: string[]; agentId?: string; sdlcPhase: SDLCPhase; collisionRisk: CollisionRisk; metadata: Record<string, any>; } export enum TaskType { DEVELOPMENT = 'development', TESTING = 'testing', BUILD = 'build', DEPLOYMENT = 'deployment', QUALITY_ASSURANCE = 'quality_assurance', DOCUMENTATION = 'documentation', ANALYSIS = 'analysis', MONITORING = 'monitoring', SECURITY = 'security' } export enum Priority { CRITICAL = 1, HIGH = 2, MEDIUM = 3, LOW = 4, BACKGROUND = 5 } export enum SDLCPhase { PLANNING = 'planning', ANALYSIS = 'analysis', DESIGN = 'design', IMPLEMENTATION = 'implementation', TESTING = 'testing', DEPLOYMENT = 'deployment', MAINTENANCE = 'maintenance' } export enum CollisionRisk { NONE = 'none', LOW = 'low', MEDIUM = 'medium', HIGH = 'high', CRITICAL = 'critical' } export interface Resource { type: ResourceType; name: string; capacity: number; exclusive: boolean; } export enum ResourceType { FILE_SYSTEM = 'file_system', DATABASE = 'database', NETWORK = 'network', CPU = 'cpu', MEMORY = 'memory', AGENT = 'agent', BUILD_SYSTEM = 'build_system', TEST_ENVIRONMENT = 'test_environment' } export interface TaskExecution { taskId: string; startTime: Date; endTime?: Date; status: ExecutionStatus; progress: number; result?: any; error?: Error; resourceUsage: ResourceUsage[]; } export enum ExecutionStatus { QUEUED = 'queued', RUNNING = 'running', COMPLETED = 'completed', FAILED = 'failed', CANCELLED = 'cancelled', PAUSED = 'paused' } export interface ResourceUsage { resourceType: ResourceType; resourceName: string; usage: number; maxUsage: number; } export interface CollisionDetectionResult { hasCollision: boolean; collisionType: CollisionType; conflictingTasks: string[]; recommendation: CollisionRecommendation; severity: CollisionSeverity; } export enum CollisionType { RESOURCE_CONFLICT = 'resource_conflict', DEPENDENCY_CYCLE = 'dependency_cycle', SDLC_PHASE_VIOLATION = 'sdlc_phase_violation', AGENT_OVERLOAD = 'agent_overload', FILE_LOCK_CONFLICT = 'file_lock_conflict', BUILD_SYSTEM_CONFLICT = 'build_system_conflict' } export enum CollisionRecommendation { SERIALIZE = 'serialize', RESCHEDULE = 'reschedule', RESOURCE_ALLOCATION = 'resource_allocation', TASK_SPLITTING = 'task_splitting', PRIORITY_ADJUSTMENT = 'priority_adjustment', AGENT_REASSIGNMENT = 'agent_reassignment' } export enum CollisionSeverity { INFO = 'info', WARNING = 'warning', ERROR = 'error', CRITICAL = 'critical' } export class ParallelTaskManager extends EventEmitter { private tasks: Map<string, Task> = new Map(); private executions: Map<string, TaskExecution> = new Map(); private resourcePool: Map<string, Resource> = new Map(); private agentWorkload: Map<string, number> = new Map(); private sdlcState: SDLCPhase = SDLCPhase.PLANNING; private maxParallelTasks: number = 10; private environmentManager: EnvironmentManager; constructor() { super(); this.environmentManager = new EnvironmentManager(); this.initializeResourcePool(); this.startResourceMonitoring(); } /** * Add a task to the execution queue with collision detection */ async addTask(task: Task): Promise<string> { this.emit('task:added', { taskId: task.id, task }); // Validate task const validation = await this.validateTask(task); if (!validation.isValid) { throw new Error(`Task validation failed: ${validation.errors.join(', ')}`); } // Detect collisions const collisionResult = await this.detectCollisions(task); if (collisionResult.hasCollision && collisionResult.severity === CollisionSeverity.CRITICAL) { throw new Error(`Critical collision detected: ${collisionResult.collisionType}`); } // Handle non-critical collisions if (collisionResult.hasCollision) { await this.handleCollision(task, collisionResult); } this.tasks.set(task.id, task); // Try to execute immediately if resources are available await this.tryExecuteTask(task.id); return task.id; } /** * Execute multiple tasks in parallel with intelligent orchestration */ async executeParallel(taskIds: string[]): Promise<Map<string, TaskExecution>> { const executionPlan = await this.createExecutionPlan(taskIds); const results = new Map<string, TaskExecution>(); // Execute tasks in parallel batches based on dependencies and resource availability for (const batch of executionPlan.batches) { const batchPromises = batch.map(taskId => this.executeTask(taskId)); const batchResults = await Promise.allSettled(batchPromises); batchResults.forEach((result, index) => { const taskId = batch[index]; if (result.status === 'fulfilled') { results.set(taskId, result.value); } else { const execution: TaskExecution = { taskId, startTime: new Date(), endTime: new Date(), status: ExecutionStatus.FAILED, progress: 0, error: result.reason, resourceUsage: [] }; results.set(taskId, execution); this.emit('task:failed', { taskId, error: result.reason }); } }); } return results; } /** * Intelligent collision detection system */ private async detectCollisions(newTask: Task): Promise<CollisionDetectionResult> { const runningTasks = Array.from(this.executions.values()) .filter(exec => exec.status === ExecutionStatus.RUNNING); const conflictingTasks: string[] = []; let collisionType = CollisionType.RESOURCE_CONFLICT; let severity = CollisionSeverity.INFO; // Check resource conflicts for (const execution of runningTasks) { const task = this.tasks.get(execution.taskId); if (!task) continue; // Check for exclusive resource conflicts const resourceConflict = this.checkResourceConflict(newTask, task); if (resourceConflict.hasConflict) { conflictingTasks.push(task.id); if (this.getSeverityLevel(CollisionSeverity.WARNING) > this.getSeverityLevel(severity)) { severity = CollisionSeverity.WARNING; } } // Check SDLC phase violations if (this.checkSDLCPhaseConflict(newTask, task)) { conflictingTasks.push(task.id); collisionType = CollisionType.SDLC_PHASE_VIOLATION; if (this.getSeverityLevel(CollisionSeverity.ERROR) > this.getSeverityLevel(severity)) { severity = CollisionSeverity.ERROR; } } // Check agent overload if (newTask.agentId && task.agentId === newTask.agentId) { const currentLoad = this.agentWorkload.get(newTask.agentId) || 0; if (currentLoad >= 3) { // Max 3 concurrent tasks per agent conflictingTasks.push(task.id); collisionType = CollisionType.AGENT_OVERLOAD; if (this.getSeverityLevel(CollisionSeverity.WARNING) > this.getSeverityLevel(severity)) { severity = CollisionSeverity.WARNING; } } } } // Check dependency cycles const hasCycle = this.checkDependencyCycle(newTask); if (hasCycle) { collisionType = CollisionType.DEPENDENCY_CYCLE; severity = CollisionSeverity.CRITICAL; } return { hasCollision: conflictingTasks.length > 0 || hasCycle, collisionType, conflictingTasks, recommendation: this.generateRecommendation(collisionType, severity), severity }; } /** * Handle collisions with intelligent resolution strategies */ private async handleCollision(task: Task, collision: CollisionDetectionResult): Promise<void> { this.emit('collision:detected', { task, collision }); switch (collision.recommendation) { case CollisionRecommendation.SERIALIZE: // Wait for conflicting tasks to complete await this.waitForTasks(collision.conflictingTasks); break; case CollisionRecommendation.RESCHEDULE: // Delay task execution task.metadata.scheduledDelay = this.calculateOptimalDelay(collision); break; case CollisionRecommendation.RESOURCE_ALLOCATION: // Adjust resource allocation await this.reallocateResources(task, collision); break; case CollisionRecommendation.AGENT_REASSIGNMENT: // Reassign to less loaded agent const alternativeAgent = await this.findAlternativeAgent(task); if (alternativeAgent) { task.agentId = alternativeAgent; } break; case CollisionRecommendation.TASK_SPLITTING: // Split task into smaller parallel chunks const subtasks = await this.splitTask(task); for (const subtask of subtasks) { await this.addTask(subtask); } break; } } /** * Create intelligent execution plan with dependency resolution */ private async createExecutionPlan(taskIds: string[]): Promise<{ batches: string[][]; totalEstimatedTime: number; resourceUtilization: Map<ResourceType, number>; }> { const tasks = taskIds.map(id => this.tasks.get(id)).filter(Boolean) as Task[]; const dependencyGraph = this.buildDependencyGraph(tasks); const batches: string[][] = []; const processed = new Set<string>(); let totalEstimatedTime = 0; const resourceUtilization = new Map<ResourceType, number>(); while (processed.size < tasks.length) { const batch: string[] = []; const batchResources = new Map<ResourceType, number>(); for (const task of tasks) { if (processed.has(task.id)) continue; // Check if all dependencies are satisfied const dependenciesSatisfied = task.dependencies.every(dep => processed.has(dep)); if (!dependenciesSatisfied) continue; // Check resource availability const canAddToBatch = this.canAddTaskToBatch(task, batchResources); if (!canAddToBatch) continue; batch.push(task.id); processed.add(task.id); // Update batch resource usage for (const resource of task.requiredResources) { const current = batchResources.get(resource.type) || 0; batchResources.set(resource.type, current + resource.capacity); } } if (batch.length === 0 && processed.size < tasks.length) { // Deadlock detection - break by priority const remainingTasks = tasks.filter(t => !processed.has(t.id)); const highestPriority = remainingTasks.reduce((min, task) => task.priority < min.priority ? task : min ); batch.push(highestPriority.id); processed.add(highestPriority.id); } batches.push(batch); // Calculate batch time (max of all tasks in batch) const batchTime = Math.max(...batch.map(id => { const task = this.tasks.get(id); return task ? task.estimatedDuration : 0; })); totalEstimatedTime += batchTime; // Update total resource utilization for (const [resourceType, usage] of batchResources) { const current = resourceUtilization.get(resourceType) || 0; resourceUtilization.set(resourceType, Math.max(current, usage)); } } return { batches, totalEstimatedTime, resourceUtilization }; } /** * Execute a single task with resource management */ private async executeTask(taskId: string): Promise<TaskExecution> { const task = this.tasks.get(taskId); if (!task) { throw new Error(`Task not found: ${taskId}`); } const execution: TaskExecution = { taskId, startTime: new Date(), status: ExecutionStatus.RUNNING, progress: 0, resourceUsage: [] }; this.executions.set(taskId, execution); this.emit('task:started', { taskId, task }); try { // Allocate resources await this.allocateResources(task); // Update agent workload if (task.agentId) { const currentLoad = this.agentWorkload.get(task.agentId) || 0; this.agentWorkload.set(task.agentId, currentLoad + 1); } // Execute task based on type const result = await this.executeTaskByType(task); execution.endTime = new Date(); execution.status = ExecutionStatus.COMPLETED; execution.progress = 100; execution.result = result; this.emit('task:completed', { taskId, task, result }); } catch (error) { execution.endTime = new Date(); execution.status = ExecutionStatus.FAILED; execution.error = error as Error; this.emit('task:failed', { taskId, task, error }); throw error; } finally { // Release resources await this.releaseResources(task); // Update agent workload if (task.agentId) { const currentLoad = this.agentWorkload.get(task.agentId) || 0; this.agentWorkload.set(task.agentId, Math.max(0, currentLoad - 1)); } } return execution; } /** * Execute task based on its type with proper agent coordination */ private async executeTaskByType(task: Task): Promise<any> { switch (task.type) { case TaskType.DEVELOPMENT: return await this.executeDevelopmentTask(task); case TaskType.TESTING: return await this.executeTestingTask(task); case TaskType.BUILD: return await this.executeBuildTask(task); case TaskType.DEPLOYMENT: return await this.executeDeploymentTask(task); case TaskType.QUALITY_ASSURANCE: return await this.executeQualityAssuranceTask(task); case TaskType.DOCUMENTATION: return await this.executeDocumentationTask(task); case TaskType.ANALYSIS: return await this.executeAnalysisTask(task); case TaskType.MONITORING: return await this.executeMonitoringTask(task); case TaskType.SECURITY: return await this.executeSecurityTask(task); default: throw new Error(`Unknown task type: ${task.type}`); } } // Task execution methods for different types private async executeDevelopmentTask(task: Task): Promise<any> { // Coordinate with appropriate agent (James-Frontend, Marcus-Backend, etc.) return { status: 'completed', type: 'development', metadata: task.metadata }; } private async executeTestingTask(task: Task): Promise<any> { // Coordinate with Maria-QA agent return { status: 'completed', type: 'testing', coverage: 85, metadata: task.metadata }; } private async executeBuildTask(task: Task): Promise<any> { // Execute build with proper resource allocation return { status: 'completed', type: 'build', buildTime: task.estimatedDuration }; } private async executeDeploymentTask(task: Task): Promise<any> { // Coordinate deployment with environment manager const env = await this.environmentManager.getCurrentEnvironment(); return { status: 'completed', type: 'deployment', environment: env }; } private async executeQualityAssuranceTask(task: Task): Promise<any> { // Run quality checks with Maria-QA return { status: 'completed', type: 'qa', qualityScore: 92 }; } private async executeDocumentationTask(task: Task): Promise<any> { // Generate documentation with Sarah-PM coordination return { status: 'completed', type: 'documentation', pages: 5 }; } private async executeAnalysisTask(task: Task): Promise<any> { // Business analysis with Alex-BA or Dr.AI-ML return { status: 'completed', type: 'analysis', insights: task.metadata.analysisType }; } private async executeMonitoringTask(task: Task): Promise<any> { // Execute monitoring task return { status: 'completed', type: 'monitoring', metrics: task.metadata.metrics }; } private async executeSecurityTask(task: Task): Promise<any> { // Execute security scan or audit return { status: 'completed', type: 'security', vulnerabilities: 0 }; } // Helper methods private initializeResourcePool(): void { const resources: Resource[] = [ { type: ResourceType.CPU, name: 'cpu-cores', capacity: 8, exclusive: false }, { type: ResourceType.MEMORY, name: 'system-memory', capacity: 16384, exclusive: false }, { type: ResourceType.FILE_SYSTEM, name: 'project-files', capacity: 1, exclusive: true }, { type: ResourceType.DATABASE, name: 'test-db', capacity: 3, exclusive: false }, { type: ResourceType.BUILD_SYSTEM, name: 'build-pipeline', capacity: 1, exclusive: true }, { type: ResourceType.TEST_ENVIRONMENT, name: 'test-env', capacity: 5, exclusive: false } ]; for (const resource of resources) { this.resourcePool.set(resource.name, resource); } } private startResourceMonitoring(): void { setInterval(() => { this.emit('resource:status', { pool: Array.from(this.resourcePool.values()), agentWorkload: Array.from(this.agentWorkload.entries()), activeTasks: this.executions.size }); }, 5000); } private getSeverityLevel(severity: CollisionSeverity): number { const levels = { [CollisionSeverity.INFO]: 1, [CollisionSeverity.WARNING]: 2, [CollisionSeverity.ERROR]: 3, [CollisionSeverity.CRITICAL]: 4 }; return levels[severity] || 0; } private async validateTask(task: Task): Promise<{ isValid: boolean; errors: string[] }> { const errors: string[] = []; if (!task.id || !task.name) { errors.push('Task must have id and name'); } if (task.estimatedDuration <= 0) { errors.push('Task must have positive estimated duration'); } if (!task.requiredResources || task.requiredResources.length === 0) { errors.push('Task must specify required resources'); } return { isValid: errors.length === 0, errors }; } private checkResourceConflict(task1: Task, task2: Task): { hasConflict: boolean } { for (const resource1 of task1.requiredResources) { for (const resource2 of task2.requiredResources) { if (resource1.name === resource2.name && (resource1.exclusive || resource2.exclusive)) { return { hasConflict: true }; } } } return { hasConflict: false }; } private checkSDLCPhaseConflict(task1: Task, task2: Task): boolean { // Define phase dependencies const phaseOrder = [ SDLCPhase.PLANNING, SDLCPhase.ANALYSIS, SDLCPhase.DESIGN, SDLCPhase.IMPLEMENTATION, SDLCPhase.TESTING, SDLCPhase.DEPLOYMENT, SDLCPhase.MAINTENANCE ]; const phase1Index = phaseOrder.indexOf(task1.sdlcPhase); const phase2Index = phaseOrder.indexOf(task2.sdlcPhase); // Check if trying to run later phase before earlier phase is complete const task2Execution = this.executions.get(task2.id); return phase1Index < phase2Index && task2Execution?.status !== ExecutionStatus.COMPLETED; } private checkDependencyCycle(task: Task): boolean { const visited = new Set<string>(); const recursionStack = new Set<string>(); const hasCycleDFS = (taskId: string): boolean => { if (recursionStack.has(taskId)) return true; if (visited.has(taskId)) return false; visited.add(taskId); recursionStack.add(taskId); const currentTask = this.tasks.get(taskId); if (currentTask) { for (const dep of currentTask.dependencies) { if (hasCycleDFS(dep)) return true; } } recursionStack.delete(taskId); return false; }; return hasCycleDFS(task.id); } private generateRecommendation( collisionType: CollisionType, severity: CollisionSeverity ): CollisionRecommendation { switch (collisionType) { case CollisionType.RESOURCE_CONFLICT: return severity >= CollisionSeverity.ERROR ? CollisionRecommendation.SERIALIZE : CollisionRecommendation.RESOURCE_ALLOCATION; case CollisionType.AGENT_OVERLOAD: return CollisionRecommendation.AGENT_REASSIGNMENT; case CollisionType.SDLC_PHASE_VIOLATION: return CollisionRecommendation.RESCHEDULE; case CollisionType.DEPENDENCY_CYCLE: return CollisionRecommendation.TASK_SPLITTING; default: return CollisionRecommendation.SERIALIZE; } } private async tryExecuteTask(taskId: string): Promise<void> { const task = this.tasks.get(taskId); if (!task) return; const runningCount = Array.from(this.executions.values()) .filter(exec => exec.status === ExecutionStatus.RUNNING).length; if (runningCount < this.maxParallelTasks) { await this.executeTask(taskId); } } private async waitForTasks(taskIds: string[]): Promise<void> { const promises = taskIds.map(id => { return new Promise<void>((resolve) => { const checkCompletion = () => { const execution = this.executions.get(id); if (!execution || execution.status === ExecutionStatus.COMPLETED || execution.status === ExecutionStatus.FAILED) { resolve(); } else { setTimeout(checkCompletion, 100); } }; checkCompletion(); }); }); await Promise.all(promises); } private calculateOptimalDelay(collision: CollisionDetectionResult): number { // Calculate delay based on collision severity and conflicting tasks const baseDelay = 1000; // 1 second const severityMultiplier = { [CollisionSeverity.INFO]: 1, [CollisionSeverity.WARNING]: 2, [CollisionSeverity.ERROR]: 5, [CollisionSeverity.CRITICAL]: 10 }; return baseDelay * severityMultiplier[collision.severity] * collision.conflictingTasks.length; } private async reallocateResources(task: Task, collision: CollisionDetectionResult): Promise<void> { // Implement resource reallocation logic // This could involve reducing resource requirements or finding alternative resources } private async findAlternativeAgent(task: Task): Promise<string | null> { // Find least loaded agent that can handle the task type const compatibleAgents = ['maria-qa', 'james-frontend', 'marcus-backend', 'sarah-pm']; let minLoad = Infinity; let bestAgent = null; for (const agentId of compatibleAgents) { const load = this.agentWorkload.get(agentId) || 0; if (load < minLoad) { minLoad = load; bestAgent = agentId; } } return bestAgent; } private async splitTask(task: Task): Promise<Task[]> { // Split task into smaller parallel subtasks const subtasks: Task[] = []; const subtaskCount = Math.min(task.estimatedDuration / 300000, 4); // Max 4 subtasks for (let i = 0; i < subtaskCount; i++) { subtasks.push({ ...task, id: `${task.id}_subtask_${i}`, name: `${task.name} (Part ${i + 1})`, estimatedDuration: task.estimatedDuration / subtaskCount, dependencies: i === 0 ? task.dependencies : [`${task.id}_subtask_${i - 1}`] }); } return subtasks; } private buildDependencyGraph(tasks: Task[]): Map<string, string[]> { const graph = new Map<string, string[]>(); for (const task of tasks) { graph.set(task.id, task.dependencies); } return graph; } private canAddTaskToBatch(task: Task, batchResources: Map<ResourceType, number>): boolean { for (const resource of task.requiredResources) { const available = this.resourcePool.get(resource.name); if (!available) continue; const currentUsage = batchResources.get(resource.type) || 0; if (currentUsage + resource.capacity > available.capacity) { return false; } if (resource.exclusive && currentUsage > 0) { return false; } } return true; } private async allocateResources(task: Task): Promise<void> { // Implement resource allocation logic for (const resource of task.requiredResources) { this.emit('resource:allocated', { taskId: task.id, resource: resource.name, capacity: resource.capacity }); } } private async releaseResources(task: Task): Promise<void> { // Implement resource release logic for (const resource of task.requiredResources) { this.emit('resource:released', { taskId: task.id, resource: resource.name, capacity: resource.capacity }); } } // Public API methods public getExecutionStatus(taskId: string): TaskExecution | undefined { return this.executions.get(taskId); } public getResourceUtilization(): Map<string, number> { const utilization = new Map<string, number>(); for (const [name, resource] of this.resourcePool) { let usage = 0; for (const execution of this.executions.values()) { if (execution.status === ExecutionStatus.RUNNING) { const task = this.tasks.get(execution.taskId); if (task) { const taskResource = task.requiredResources.find(r => r.name === name); if (taskResource) { usage += taskResource.capacity; } } } } utilization.set(name, (usage / resource.capacity) * 100); } return utilization; } public getAgentWorkload(): Map<string, number> { return new Map(this.agentWorkload); } public async cancelTask(taskId: string): Promise<void> { const execution = this.executions.get(taskId); if (execution && execution.status === ExecutionStatus.RUNNING) { execution.status = ExecutionStatus.CANCELLED; execution.endTime = new Date(); const task = this.tasks.get(taskId); if (task) { await this.releaseResources(task); } this.emit('task:cancelled', { taskId }); } } public async pauseTask(taskId: string): Promise<void> { const execution = this.executions.get(taskId); if (execution && execution.status === ExecutionStatus.RUNNING) { execution.status = ExecutionStatus.PAUSED; this.emit('task:paused', { taskId }); } } public async resumeTask(taskId: string): Promise<void> { const execution = this.executions.get(taskId); if (execution && execution.status === ExecutionStatus.PAUSED) { execution.status = ExecutionStatus.RUNNING; this.emit('task:resumed', { taskId }); } } } export default ParallelTaskManager;