mcp-adr-analysis-server
Version:
MCP server for analyzing Architectural Decision Records and project architecture
265 lines • 10.6 kB
JavaScript
/**
* Pattern to DAG Converter
*
* Converts validated patterns (YAML definitions) into executable DAG task nodes
* for the bootstrap validation loop's infrastructure layer.
*/
import { EnhancedLogger } from './enhanced-logging.js';
/**
* Convert validated pattern to infrastructure DAG tasks
*/
export class PatternToDAGConverter {
logger;
constructor() {
this.logger = new EnhancedLogger();
}
/**
* Build infrastructure DAG tasks from validated pattern
*
* This is the KEY method that auto-generates DAG tasks from pattern YAML
*/
buildInfrastructureTasksFromPattern(pattern, platform) {
const tasks = [];
this.logger.info(`🔨 Building infrastructure DAG tasks for platform: ${platform}`, 'PatternToDAGConverter');
// 1. Add dependency installation tasks (highest priority)
if (pattern.dependencies) {
this.logger.info(`📦 Adding ${pattern.dependencies.length} dependency installation tasks`, 'PatternToDAGConverter');
for (const dep of pattern.dependencies) {
if (dep.required) {
// Install task
if (dep.installCommand) {
const installTask = this.createDependencyInstallTask(dep, platform);
tasks.push(installTask);
}
// Verification task
if (dep.verificationCommand) {
const verifyTask = this.createDependencyVerificationTask(dep, platform);
tasks.push(verifyTask);
}
}
}
}
// 2. Build tasks from deploymentPhases (core infrastructure setup)
if (pattern.deploymentPhases) {
this.logger.info(`🏗️ Processing ${pattern.deploymentPhases.length} deployment phases`, 'PatternToDAGConverter');
for (const phase of pattern.deploymentPhases) {
const isInfraPhase = this.isInfrastructurePhase(phase);
if (isInfraPhase) {
const phaseTasks = this.createTasksFromPhase(phase, pattern, platform);
tasks.push(...phaseTasks);
}
}
}
// 3. Add validation checks as DAG tasks
if (pattern.validationChecks) {
this.logger.info(`✅ Adding ${pattern.validationChecks.length} validation check tasks`, 'PatternToDAGConverter');
for (const check of pattern.validationChecks) {
const isInfraCheck = this.isInfrastructureValidationCheck(check);
if (isInfraCheck) {
const checkTask = this.createValidationCheckTask(check, tasks, platform);
tasks.push(checkTask);
}
}
}
this.logger.info(`✅ Generated ${tasks.length} infrastructure DAG tasks from pattern`, 'PatternToDAGConverter');
return tasks;
}
/**
* Create dependency installation task
*/
createDependencyInstallTask(dependency, platform) {
const cmd = dependency.installCommand;
const parts = cmd.split(' ').filter(p => p.trim());
const task = {
id: `${platform}-install-${this.sanitizeTaskId(dependency.name)}`,
name: `Install ${dependency.name}`,
description: `Install required dependency: ${dependency.name}`,
commandArgs: parts.slice(1),
expectedExitCode: 0,
dependsOn: [],
category: 'infrastructure',
severity: 'critical',
timeout: 120000, // 2 minutes for installations
};
if (parts[0]) {
task.command = parts[0];
}
return task;
}
/**
* Create dependency verification task
*/
createDependencyVerificationTask(dependency, platform) {
const cmd = dependency.verificationCommand;
const parts = cmd.split(' ').filter(p => p.trim());
const task = {
id: `${platform}-verify-${this.sanitizeTaskId(dependency.name)}`,
name: `Verify ${dependency.name}`,
description: `Verify ${dependency.name} is properly installed`,
commandArgs: parts.slice(1),
expectedExitCode: 0,
dependsOn: [`${platform}-install-${this.sanitizeTaskId(dependency.name)}`],
category: 'infrastructure',
severity: 'critical',
timeout: 30000,
};
if (parts[0]) {
task.command = parts[0];
}
return task;
}
/**
* Create tasks from a deployment phase
*/
createTasksFromPhase(phase, pattern, platform) {
const tasks = [];
for (const cmd of phase.commands) {
const taskId = `${platform}-phase-${phase.order}-${this.sanitizeTaskId(cmd.description)}`;
const parts = cmd.command.split(' ').filter(p => p.trim());
// Map phase prerequisites to task dependencies
const dependencies = this.mapPrerequisitesToDependencies(phase.prerequisites, pattern.deploymentPhases, platform);
const task = {
id: taskId,
name: cmd.description,
description: cmd.description,
commandArgs: parts.slice(1),
expectedExitCode: cmd.expectedExitCode ?? 0,
dependsOn: dependencies,
category: 'infrastructure',
severity: phase.order === 1 ? 'critical' : 'error',
timeout: this.parseDuration(phase.estimatedDuration),
canFailSafely: phase.order > 2, // Later phases can fail without stopping
};
if (parts[0]) {
task.command = parts[0];
}
tasks.push(task);
}
return tasks;
}
/**
* Create validation check task
*/
createValidationCheckTask(check, existingTasks, platform) {
const parts = check.command.split(' ').filter(p => p.trim());
// Validation checks depend on all deployment tasks
const deploymentTaskIds = existingTasks
.filter(t => t.category === 'infrastructure' && !t.id.startsWith(`${platform}-validate-`))
.map(t => t.id);
const task = {
id: `${platform}-validate-${check.id}`,
name: check.name,
description: check.description,
commandArgs: parts.slice(1),
expectedExitCode: check.expectedExitCode ?? 0,
dependsOn: deploymentTaskIds,
category: 'infrastructure',
severity: check.severity,
timeout: 30000,
validationCheck: this.createValidationCheckFunction(check),
};
if (parts[0]) {
task.command = parts[0];
}
return task;
}
/**
* Determine if a deployment phase is infrastructure-related
*/
isInfrastructurePhase(phase) {
const name = phase.name.toLowerCase();
return (name.includes('infrastructure') ||
name.includes('prerequisite') ||
name.includes('setup') ||
name.includes('namespace') ||
name.includes('validation') ||
name.includes('cluster') ||
name.includes('environment') ||
phase.order <= 2 // First 2 phases are typically infrastructure
);
}
/**
* Determine if a validation check is infrastructure-related
*/
isInfrastructureValidationCheck(check) {
const id = check.id.toLowerCase();
const name = check.name.toLowerCase();
return (id.includes('cluster') ||
id.includes('node') ||
id.includes('connection') ||
id.includes('infrastructure') ||
name.includes('cluster') ||
name.includes('node') ||
name.includes('connectivity') ||
check.severity === 'critical');
}
/**
* Map phase prerequisites to task dependencies
*/
mapPrerequisitesToDependencies(prerequisites, allPhases, platform) {
const dependencies = [];
for (const prereq of prerequisites) {
// Find the phase with this name
const phase = allPhases.find(p => p.name === prereq);
if (phase) {
// Add all tasks from that phase as dependencies
for (const cmd of phase.commands) {
const taskId = `${platform}-phase-${phase.order}-${this.sanitizeTaskId(cmd.description)}`;
dependencies.push(taskId);
}
}
}
return dependencies;
}
/**
* Create validation check function from validation check definition
*/
createValidationCheckFunction(check) {
return (output) => {
const outputLower = output.toLowerCase();
// Check for explicit failure keywords
const failureKeywords = ['error', 'failed', 'not found', 'cannot', 'unable', 'denied'];
const hasFailure = failureKeywords.some(keyword => outputLower.includes(keyword));
if (hasFailure) {
return false;
}
// Check for success indicators based on check type
if (check.id.includes('deployment') || check.id.includes('ready')) {
return outputLower.includes('ready') || outputLower.includes('running');
}
if (check.id.includes('endpoint') || check.id.includes('service')) {
// Service endpoints should have IP addresses
return /\d+\.\d+\.\d+\.\d+/.test(output);
}
// Default: no failure keywords = success
return true;
};
}
/**
* Parse duration string to milliseconds
*/
parseDuration(duration) {
const match = duration.match(/(\d+)\s*(minutes?|mins?|seconds?|secs?)/i);
if (!match || !match[1] || !match[2]) {
return 30000; // Default 30 seconds
}
const value = parseInt(match[1], 10);
const unit = match[2].toLowerCase();
if (unit.startsWith('min')) {
return value * 60 * 1000;
}
else {
return value * 1000;
}
}
/**
* Sanitize task ID (remove special characters)
*/
sanitizeTaskId(description) {
return description
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');
}
}
//# sourceMappingURL=pattern-to-dag-converter.js.map