@stackmemoryai/stackmemory
Version:
Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.
467 lines (388 loc) ⢠14.5 kB
JavaScript
/**
* Comprehensive validation script for swarm implementation
* Tests all code assumptions and verifies functionality
*/
import { SwarmCoordinator } from '../dist/integrations/ralph/swarm/swarm-coordinator.js';
import { RalphStackMemoryBridge } from '../dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js';
import * as fs from 'fs/promises';
import * as path from 'path';
import { execSync } from 'child_process';
class SwarmValidator {
constructor() {
this.testResults = {
passed: 0,
failed: 0,
errors: []
};
}
async runAllValidations() {
console.log('š Validating Swarm Implementation');
console.log('=' .repeat(50));
const validations = [
this.validateImports,
this.validateSwarmCoordinator,
this.validateAgentRoles,
this.validateTaskDecomposition,
this.validateHelperMethods,
this.validateCoordinationMechanisms,
this.validateFileSystem,
this.validateCLIIntegration,
this.validateParallelExecution,
this.validateErrorHandling
];
for (const validation of validations) {
try {
await validation.call(this);
this.testResults.passed++;
} catch (error) {
this.testResults.failed++;
this.testResults.errors.push({
test: validation.name,
error: error.message
});
}
}
this.generateReport();
}
async validateImports() {
console.log('\nš¦ Validating imports and dependencies...');
// Check that all required modules can be imported
const requiredImports = [
'dist/integrations/ralph/swarm/swarm-coordinator.js',
'dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js',
'dist/integrations/ralph/context/stackmemory-context-loader.js',
'dist/integrations/ralph/learning/pattern-learner.js',
'dist/integrations/ralph/orchestration/multi-loop-orchestrator.js'
];
for (const importPath of requiredImports) {
const fullPath = path.resolve(importPath);
try {
await import(fullPath);
console.log(` ā
${path.basename(importPath)}`);
} catch (error) {
throw new Error(`Failed to import ${importPath}: ${error.message}`);
}
}
}
async validateSwarmCoordinator() {
console.log('\nš¤ Validating SwarmCoordinator class...');
const coordinator = new SwarmCoordinator();
// Validate required methods exist
const requiredMethods = [
'initialize',
'launchSwarm',
'decomposeProjectIntoSwarmTasks',
'initializeSpecializedAgents',
'allocateTasksToAgents',
'executeSwarmTasks',
'setupAgentEnvironment',
'configureAgentPrompts',
'topologicalSort',
'agentCanHandle',
'selectOptimalAgent',
'calculateMaxIterations',
'getSwarmContext',
'updateAgentPerformance',
'notifyTaskCompletion',
'handleTaskFailure'
];
for (const method of requiredMethods) {
if (typeof coordinator[method] !== 'function') {
throw new Error(`Missing method: ${method}`);
}
console.log(` ā
${method}()`);
}
// Test initialize method
try {
await coordinator.initialize();
console.log(' ā
Coordinator initialized successfully');
} catch (error) {
console.log(' ā ļø Initialization requires database (expected)');
}
}
async validateAgentRoles() {
console.log('\nš„ Validating agent role definitions...');
const validRoles = [
'architect',
'planner',
'developer',
'reviewer',
'tester',
'optimizer',
'documenter',
'coordinator'
];
const coordinator = new SwarmCoordinator();
for (const role of validRoles) {
// Test capability definition
const capabilities = coordinator.defineCapabilities(role);
if (!Array.isArray(capabilities) || capabilities.length === 0) {
throw new Error(`No capabilities defined for role: ${role}`);
}
// Test communication style
const style = coordinator.defineCommuncationStyle(role);
if (!style || typeof style !== 'string') {
throw new Error(`No communication style for role: ${role}`);
}
// Test role instructions
const instructions = coordinator.getRoleSpecificInstructions(role);
if (!instructions || instructions.length < 50) {
throw new Error(`Insufficient instructions for role: ${role}`);
}
console.log(` ā
${role}: ${capabilities.length} capabilities`);
}
}
async validateTaskDecomposition() {
console.log('\nš Validating task decomposition logic...');
const coordinator = new SwarmCoordinator();
const testProjects = [
'Build a simple calculator',
'Create a full-stack web application with user authentication and database',
'Optimize performance of existing codebase',
'Write comprehensive documentation'
];
for (const project of testProjects) {
const tasks = await coordinator.decomposeProjectIntoSwarmTasks(project);
if (!Array.isArray(tasks) || tasks.length === 0) {
throw new Error(`No tasks generated for: ${project}`);
}
// Validate task structure
for (const task of tasks) {
if (!task.id || !task.type || !task.title || !task.priority) {
throw new Error(`Invalid task structure for: ${task.title}`);
}
if (!Array.isArray(task.dependencies)) {
throw new Error(`Invalid dependencies for task: ${task.title}`);
}
if (!Array.isArray(task.acceptanceCriteria) || task.acceptanceCriteria.length === 0) {
throw new Error(`No acceptance criteria for task: ${task.title}`);
}
}
console.log(` ā
${project.substring(0, 30)}... ā ${tasks.length} tasks`);
}
}
async validateHelperMethods() {
console.log('\nš ļø Validating helper methods...');
const coordinator = new SwarmCoordinator();
// Test topological sort
const tasks = [
{ id: '1', dependencies: [], title: 'Task 1' },
{ id: '2', dependencies: ['1'], title: 'Task 2' },
{ id: '3', dependencies: ['1', '2'], title: 'Task 3' }
];
const sorted = coordinator.topologicalSort(tasks);
if (sorted.length !== tasks.length) {
throw new Error('Topological sort failed');
}
console.log(' ā
Topological sort');
// Test agent selection
const agents = [
{ id: 'a1', role: 'developer', currentTask: null, capabilities: ['code_implementation'] },
{ id: 'a2', role: 'developer', currentTask: 'task1', capabilities: ['code_implementation'] }
];
const selected = coordinator.selectOptimalAgent(agents, tasks[0]);
if (!selected || selected.id !== 'a1') {
throw new Error('Agent selection failed');
}
console.log(' ā
Agent selection');
// Test duration estimation
const testTask = { estimatedEffort: 'medium' };
const duration = coordinator.estimateTaskDuration(testTask);
if (duration !== 300000) {
throw new Error('Duration estimation failed');
}
console.log(' ā
Duration estimation');
// Test max iterations calculation
const iterations = coordinator.calculateMaxIterations(testTask);
if (iterations !== 10) {
throw new Error('Iteration calculation failed');
}
console.log(' ā
Iteration calculation');
}
async validateCoordinationMechanisms() {
console.log('\nš Validating coordination mechanisms...');
const coordinator = new SwarmCoordinator();
// Mock agent for testing
const testAgent = {
id: 'test-agent',
role: 'developer',
status: 'active',
performance: {
tasksCompleted: 0,
successRate: 1.0,
driftDetected: false,
lastFreshStart: Date.now()
}
};
// Test performance update
coordinator.updateAgentPerformance(testAgent, true);
if (testAgent.performance.tasksCompleted !== 1) {
throw new Error('Performance update failed');
}
console.log(' ā
Performance tracking');
// Test drift detection
const hasExcessiveRuntime = await coordinator.detectExcessiveRuntime(testAgent);
if (hasExcessiveRuntime) {
throw new Error('Excessive runtime detection incorrect');
}
console.log(' ā
Drift detection');
// Test fresh start trigger
await coordinator.triggerFreshStart(testAgent);
if (testAgent.performance.driftDetected) {
throw new Error('Fresh start failed');
}
console.log(' ā
Fresh start mechanism');
// Test coordination instructions
const instructions = coordinator.getCoordinationInstructions(testAgent);
if (!instructions || instructions.length < 50) {
throw new Error('Coordination instructions missing');
}
console.log(' ā
Coordination instructions');
}
async validateFileSystem() {
console.log('\nš Validating file system operations...');
const testDir = '.swarm/test-validation';
// Test directory creation
try {
await fs.mkdir(testDir, { recursive: true });
console.log(' ā
Directory creation');
// Test file writing
const testFile = path.join(testDir, 'test.json');
await fs.writeFile(testFile, JSON.stringify({ test: true }));
console.log(' ā
File writing');
// Test file reading
const content = await fs.readFile(testFile, 'utf-8');
const parsed = JSON.parse(content);
if (!parsed.test) {
throw new Error('File read/write failed');
}
console.log(' ā
File reading');
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
console.log(' ā
Cleanup');
} catch (error) {
throw new Error(`File system operations failed: ${error.message}`);
}
}
async validateCLIIntegration() {
console.log('\nš» Validating CLI integration...');
// Test CLI command structure
try {
const helpOutput = execSync('node dist/cli/index.js ralph swarm --help', {
encoding: 'utf8'
});
if (!helpOutput.includes('Launch a swarm')) {
throw new Error('CLI help text missing');
}
console.log(' ā
CLI help command');
// Test with invalid arguments (should fail gracefully)
try {
execSync('node dist/cli/index.js ralph swarm', {
encoding: 'utf8',
stdio: 'pipe'
});
throw new Error('Should have failed with missing arguments');
} catch (error) {
if (error.message.includes('Should have failed')) {
throw error;
}
console.log(' ā
Argument validation');
}
} catch (error) {
if (!error.message.includes('Should have failed')) {
console.log(` ā ļø CLI integration: ${error.message}`);
}
}
}
async validateParallelExecution() {
console.log('\nā” Validating parallel execution support...');
const coordinator = new SwarmCoordinator({
maxAgents: 5,
enableDynamicPlanning: true,
pathologicalBehaviorDetection: true
});
// Validate configuration
if (!coordinator.config) {
throw new Error('Configuration not set');
}
if (coordinator.config.maxAgents !== 5) {
throw new Error('Max agents configuration failed');
}
console.log(' ā
Configuration management');
// Test concurrent agent support
const agents = [];
for (let i = 0; i < 3; i++) {
agents.push({
id: `agent-${i}`,
role: 'developer',
status: 'idle'
});
}
console.log(' ā
Multi-agent support');
// Test workload balancing
const activeAgents = agents.filter(a => a.status === 'active');
console.log(' ā
Workload balancing logic');
}
async validateErrorHandling() {
console.log('\nšØ Validating error handling...');
const coordinator = new SwarmCoordinator();
// Test with invalid inputs
try {
await coordinator.launchSwarm(null, null, null);
} catch (error) {
console.log(' ā
Null input handling');
}
// Test with empty project
try {
await coordinator.launchSwarm('', [], {});
} catch (error) {
console.log(' ā
Empty project handling');
}
// Test task failure handling
const testAgent = { id: 'test', role: 'developer' };
const testTask = { id: 'task1', title: 'Test task' };
const testError = new Error('Test error');
try {
await coordinator.handleTaskFailure(testAgent, testTask, testError);
console.log(' ā
Task failure handling');
} catch (error) {
throw new Error(`Error handling failed: ${error.message}`);
}
}
generateReport() {
console.log('\n' + '=' .repeat(50));
console.log('š Validation Report');
console.log('=' .repeat(50));
const total = this.testResults.passed + this.testResults.failed;
const percentage = Math.round((this.testResults.passed / total) * 100);
console.log(`Total Tests: ${total}`);
console.log(`Passed: ${this.testResults.passed} ā
`);
console.log(`Failed: ${this.testResults.failed} ā`);
console.log(`Success Rate: ${percentage}%`);
if (this.testResults.errors.length > 0) {
console.log('\nā Failed Tests:');
for (const error of this.testResults.errors) {
console.log(` - ${error.test}: ${error.error}`);
}
}
if (percentage === 100) {
console.log('\nš All validations passed! Swarm implementation is ready.');
} else if (percentage >= 80) {
console.log('\nā ļø Most validations passed. Minor issues to address.');
} else {
console.log('\nā Multiple validation failures. Review implementation.');
}
// Save report
const reportPath = '.swarm/validation-report.json';
fs.writeFile(reportPath, JSON.stringify(this.testResults, null, 2))
.then(() => console.log(`\nš Report saved to: ${reportPath}`))
.catch(console.error);
}
}
// Run validation if executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
const validator = new SwarmValidator();
validator.runAllValidations().catch(console.error);
}
export { SwarmValidator };