agentic-qe
Version:
Agentic Quality Engineering Fleet System - AI-driven quality management platform
522 lines • 23.7 kB
JavaScript
"use strict";
/**
* QualityGateAgent - Intelligent quality gate evaluation with decision trees
* Implements SPARC Phase 2 Section 7.1 - Intelligent Quality Gate Algorithm
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.QualityGateAgent = void 0;
const events_1 = require("events");
const types_1 = require("../types");
class QualityGateAgent extends events_1.EventEmitter {
constructor(id, memoryStore) {
super();
this.status = types_1.AgentStatus.INITIALIZING;
// Default quality criteria based on industry standards
this.defaultCriteria = [
{ name: 'test_coverage', threshold: 0.85, weight: 0.25, type: 'minimum_threshold', critical: true },
{ name: 'test_success_rate', threshold: 0.95, weight: 0.30, type: 'minimum_threshold', critical: true },
{ name: 'security_vulnerabilities', threshold: 0, weight: 0.20, type: 'maximum_threshold', critical: true },
{ name: 'performance_regression', threshold: 0.10, weight: 0.15, type: 'maximum_threshold', critical: false },
{ name: 'code_quality_score', threshold: 0.80, weight: 0.10, type: 'minimum_threshold', critical: false }
];
this.id = id;
this.memoryStore = memoryStore;
this.decisionEngine = new DecisionEngine();
this.consciousnessEngine = new ConsciousnessEngine();
this.psychoSymbolicReasoner = new PsychoSymbolicReasoner();
this.riskAnalyzer = new RiskAnalyzer();
}
// ============================================================================
// Agent Lifecycle
// ============================================================================
async initialize() {
try {
this.status = types_1.AgentStatus.INITIALIZING;
// Initialize decision engines
await this.decisionEngine.initialize();
await this.consciousnessEngine.initialize();
await this.psychoSymbolicReasoner.initialize();
await this.riskAnalyzer.initialize();
// Load historical decision patterns
await this.loadDecisionPatterns();
// Store initialization state
if (this.memoryStore) {
await this.memoryStore.set('quality-gate-initialized', true, 'agents');
}
this.status = types_1.AgentStatus.IDLE;
this.emit('agent.initialized', { agentId: this.id });
}
catch (error) {
this.status = types_1.AgentStatus.ERROR;
this.emit('agent.error', { agentId: this.id, error });
throw error;
}
}
async executeTask(task) {
const request = task.payload;
return await this.evaluateQualityGate(request);
}
async terminate() {
try {
this.status = types_1.AgentStatus.STOPPING;
// Save learned decision patterns
await this.saveDecisionPatterns();
// Cleanup resources
await this.decisionEngine.cleanup();
await this.consciousnessEngine.cleanup();
await this.psychoSymbolicReasoner.cleanup();
await this.riskAnalyzer.cleanup();
this.status = types_1.AgentStatus.STOPPED;
this.emit('agent.terminated', { agentId: this.id });
}
catch (error) {
this.status = types_1.AgentStatus.ERROR;
throw error;
}
}
getStatus() {
return {
agentId: this.id,
status: this.status,
capabilities: ['quality-evaluation', 'risk-analysis', 'decision-making'],
performance: {
decisionsEvaluated: this.decisionEngine.getDecisionCount(),
averageDecisionTime: this.decisionEngine.getAverageTime(),
lastDecisionConfidence: this.decisionEngine.getLastConfidence()
}
};
}
// ============================================================================
// Core Quality Gate Evaluation - SPARC Algorithm 7.1
// ============================================================================
/**
* Evaluate quality gate using intelligent decision tree
* Based on SPARC Phase 2 Algorithm: EvaluateQualityGate
*/
async evaluateQualityGate(request) {
const startTime = Date.now();
try {
this.status = types_1.AgentStatus.ACTIVE;
// Phase 1: Initialize Decision Tree using Consciousness Framework
const decisionTree = await this.consciousnessEngine.buildDecisionTree({
context: request.context,
historicalDecisions: await this.getHistoricalGateDecisions(),
adaptationLevel: 0.8
});
// Phase 2: Collect and Normalize Metrics
const normalizedMetrics = await this.normalizeQualityMetrics(request.metrics);
// Phase 3: Apply Psycho-Symbolic Reasoning for Complex Cases
const complexityIndicators = await this.detectComplexityIndicators(request.testResults, request.metrics);
if (complexityIndicators.high) {
const reasoningResult = await this.psychoSymbolicReasoner.reason({
query: 'Should deployment proceed given complex quality state?',
context: {
testResults: request.testResults,
metrics: normalizedMetrics,
deploymentContext: request.context,
complexityFactors: complexityIndicators
},
depth: 5
});
if (reasoningResult.confidence < 0.7) {
// Escalate to human review
return {
decision: 'ESCALATE',
score: reasoningResult.confidence,
threshold: 0.7,
criteriaEvaluations: [],
riskFactors: await this.analyzeRiskFactors(request.context, []),
explanation: reasoningResult.reasoning,
recommendations: ['Human review required due to complex quality state'],
confidence: reasoningResult.confidence,
metadata: {
evaluationTime: new Date(),
context: request.context,
decisionTreeVersion: decisionTree.version
}
};
}
}
// Phase 4: Evaluate Core Quality Criteria
const criteria = request.customCriteria || this.defaultCriteria;
const evaluationResults = [];
let totalScore = 0.0;
for (const criterion of criteria) {
const metricValue = await this.getMetricValue(normalizedMetrics, criterion.name);
const passed = await this.evaluateCriterion(criterion, metricValue);
const score = await this.calculateCriterionScore(criterion, metricValue);
const evaluationResult = {
criterion,
value: metricValue,
passed,
score,
impact: await this.calculateImpact(criterion, metricValue, request.context)
};
evaluationResults.push(evaluationResult);
totalScore += score * criterion.weight;
}
// Phase 5: Apply Dynamic Threshold Adjustment
const adjustedThreshold = await this.calculateDynamicThreshold(request.context, normalizedMetrics);
// Phase 6: Make Final Decision
const baseDecision = totalScore >= adjustedThreshold ? 'PASS' : 'FAIL';
// Phase 7: Apply Risk-Based Overrides
const riskFactors = await this.analyzeRiskFactors(request.context, evaluationResults);
const finalDecision = await this.applyRiskBasedLogic(baseDecision, riskFactors);
// Phase 8: Generate Explanation and Recommendations
const explanation = await this.generateDecisionExplanation(evaluationResults, finalDecision);
const recommendations = await this.generateQualityRecommendations(evaluationResults, request.context);
const gateDecision = {
decision: finalDecision,
score: totalScore,
threshold: adjustedThreshold,
criteriaEvaluations: evaluationResults,
riskFactors,
explanation,
recommendations,
confidence: await this.calculateDecisionConfidence(evaluationResults),
metadata: {
evaluationTime: new Date(),
context: request.context,
decisionTreeVersion: decisionTree.version
}
};
// Phase 9: Learn from Decision
await this.storeLearningData(gateDecision, request.testResults, request.metrics);
// Store decision for future learning
await this.storeDecisionResult(gateDecision, Date.now() - startTime);
this.status = types_1.AgentStatus.IDLE;
return gateDecision;
}
catch (error) {
this.status = types_1.AgentStatus.ERROR;
throw error;
}
}
// ============================================================================
// Criterion Evaluation Methods
// ============================================================================
async evaluateCriterion(criterion, value) {
switch (criterion.type) {
case 'minimum_threshold':
return value >= criterion.threshold;
case 'maximum_threshold':
return value <= criterion.threshold;
case 'range':
// Range criteria would have minThreshold and maxThreshold
return value >= criterion.threshold; // Simplified
case 'trend':
return await this.evaluateTrendCriterion(criterion, value);
default:
return false;
}
}
async calculateCriterionScore(criterion, value) {
switch (criterion.type) {
case 'minimum_threshold':
return Math.min(1.0, value / criterion.threshold);
case 'maximum_threshold':
return value <= criterion.threshold ? 1.0 : Math.max(0.0, 1.0 - (value - criterion.threshold));
default:
return value >= criterion.threshold ? 1.0 : 0.0;
}
}
async calculateDynamicThreshold(context, metrics) {
let baseThreshold = 0.8; // Default threshold
// Adjust based on deployment criticality
if (context.criticality === 'critical') {
baseThreshold += 0.1;
}
else if (context.criticality === 'low') {
baseThreshold -= 0.05;
}
// Adjust based on historical performance
const historicalPerformance = await this.getHistoricalPerformance(context.environment);
if (historicalPerformance > 0.9) {
baseThreshold -= 0.02; // Slight relaxation for stable systems
}
// Adjust based on change magnitude
const changeMagnitude = await this.calculateChangeMagnitude(context.changes);
if (changeMagnitude > 0.5) {
baseThreshold += 0.05; // Higher standards for large changes
}
// Ensure threshold stays within bounds
return Math.max(0.5, Math.min(0.95, baseThreshold));
}
// ============================================================================
// Risk Analysis
// ============================================================================
async analyzeRiskFactors(context, evaluations) {
const riskFactors = [];
// Analyze deployment risk
if (context.deploymentTarget === 'production' && context.criticality === 'critical') {
riskFactors.push({
type: 'deployment-risk',
severity: 'high',
probability: 0.3,
impact: 'Production outage potential',
mitigation: ['Additional testing', 'Staged deployment', 'Rollback plan']
});
}
// Analyze change risk
const highComplexityChanges = context.changes.filter((c) => c.complexity > 8);
if (highComplexityChanges.length > 0) {
riskFactors.push({
type: 'complexity-risk',
severity: 'medium',
probability: 0.4,
impact: 'Increased defect probability',
mitigation: ['Code review', 'Additional testing', 'Monitoring']
});
}
// Analyze test quality risk
const failedTests = evaluations.filter(e => !e.passed && e.criterion.critical);
if (failedTests.length > 0) {
riskFactors.push({
type: 'test-quality-risk',
severity: 'high',
probability: 0.7,
impact: 'Critical quality criteria not met',
mitigation: ['Fix failing tests', 'Increase coverage', 'Review test strategy']
});
}
return riskFactors;
}
async applyRiskBasedLogic(baseDecision, riskFactors) {
// High-risk scenarios override positive decisions
const criticalRisks = riskFactors.filter(rf => rf.severity === 'critical');
const highRisks = riskFactors.filter(rf => rf.severity === 'high');
if (criticalRisks.length > 0) {
return 'FAIL';
}
if (highRisks.length > 1 && baseDecision === 'PASS') {
return 'ESCALATE'; // Multiple high risks require human judgment
}
return baseDecision;
}
// ============================================================================
// Metrics and Analysis
// ============================================================================
async normalizeQualityMetrics(metrics) {
return {
test_coverage: (metrics.coverage.line + metrics.coverage.branch + metrics.coverage.function) / 300, // Normalize to 0-1
test_success_rate: metrics.testResults.passed / Math.max(1, metrics.testResults.total),
security_vulnerabilities: metrics.security.vulnerabilities,
performance_regression: Math.max(0, metrics.performance.averageResponseTime - 1000) / 5000, // Normalize response time
code_quality_score: Math.min(1, metrics.performance.throughput / 1000) // Normalize throughput
};
}
async getMetricValue(normalizedMetrics, metricName) {
return normalizedMetrics[metricName] || 0;
}
async detectComplexityIndicators(testResults, metrics) {
// Detect if the quality state is complex enough to require special reasoning
const failureRate = 1 - (metrics.testResults.passed / Math.max(1, metrics.testResults.total));
const vulnerabilityCount = metrics.security.vulnerabilities;
const performanceIssues = metrics.performance.errorRate > 0.05;
const complexityScore = failureRate * 0.4 +
(vulnerabilityCount > 0 ? 0.3 : 0) +
(performanceIssues ? 0.3 : 0);
return {
high: complexityScore > 0.6,
score: complexityScore,
factors: {
highFailureRate: failureRate > 0.1,
securityVulnerabilities: vulnerabilityCount > 0,
performanceIssues
}
};
}
// ============================================================================
// Decision Support Methods
// ============================================================================
async generateDecisionExplanation(evaluations, decision) {
const passedCriteria = evaluations.filter(e => e.passed).length;
const totalCriteria = evaluations.length;
const criticalFailures = evaluations.filter(e => !e.passed && e.criterion.critical);
let explanation = `Quality gate evaluation completed. ${passedCriteria}/${totalCriteria} criteria passed. `;
if (decision === 'PASS') {
explanation += 'All critical quality criteria met. Deployment approved.';
}
else if (decision === 'FAIL') {
if (criticalFailures.length > 0) {
explanation += `Critical failures in: ${criticalFailures.map(cf => cf.criterion.name).join(', ')}. `;
}
explanation += 'Quality standards not met. Deployment blocked.';
}
else if (decision === 'ESCALATE') {
explanation += 'Complex quality state detected. Human review required.';
}
return explanation;
}
async generateQualityRecommendations(evaluations, context) {
const recommendations = [];
// Analyze failed criteria
const failedEvaluations = evaluations.filter(e => !e.passed);
for (const failed of failedEvaluations) {
switch (failed.criterion.name) {
case 'test_coverage':
recommendations.push('Increase test coverage by adding unit tests for uncovered code paths');
break;
case 'test_success_rate':
recommendations.push('Fix failing tests before deployment');
break;
case 'security_vulnerabilities':
recommendations.push('Address security vulnerabilities identified in scan');
break;
case 'performance_regression':
recommendations.push('Optimize performance or investigate regression causes');
break;
case 'code_quality_score':
recommendations.push('Improve code quality through refactoring and linting');
break;
}
}
// Context-specific recommendations
if (context.criticality === 'critical' && failedEvaluations.length > 0) {
recommendations.push('Consider additional manual testing for critical deployment');
}
if (context.changes.length > 10) {
recommendations.push('Large changeset detected - consider breaking into smaller deployments');
}
return recommendations;
}
async calculateDecisionConfidence(evaluations) {
// Calculate confidence based on how clearly criteria are met or failed
let confidenceSum = 0;
let weightSum = 0;
for (const evaluation of evaluations) {
// Higher confidence when values are clearly above/below thresholds
const distance = Math.abs(evaluation.value - evaluation.criterion.threshold);
const normalizedDistance = Math.min(1, distance / evaluation.criterion.threshold);
const confidence = 0.5 + (normalizedDistance * 0.5);
confidenceSum += confidence * evaluation.criterion.weight;
weightSum += evaluation.criterion.weight;
}
return weightSum > 0 ? confidenceSum / weightSum : 0.5;
}
// ============================================================================
// Learning and Storage
// ============================================================================
async storeLearningData(decision, testResults, metrics) {
if (this.memoryStore) {
const learningData = {
decision: decision.decision,
score: decision.score,
confidence: decision.confidence,
testResultsSummary: {
total: testResults.length,
passed: testResults.filter(t => t.status === 'passed').length,
failed: testResults.filter(t => t.status === 'failed').length
},
metricsSummary: {
coverage: metrics.coverage,
testResults: metrics.testResults,
performance: metrics.performance,
security: metrics.security
},
timestamp: new Date()
};
await this.memoryStore.set(`learning-${Date.now()}`, learningData, 'quality-gate-learning');
}
}
// Placeholder implementations for complex methods
async loadDecisionPatterns() {
if (this.memoryStore) {
const patterns = await this.memoryStore.get('decision-patterns', 'agents');
// Apply loaded patterns
}
}
async saveDecisionPatterns() {
if (this.memoryStore) {
await this.memoryStore.set('decision-patterns', {
timestamp: new Date(),
patterns: []
}, 'agents');
}
}
async storeDecisionResult(decision, duration) {
if (this.memoryStore) {
await this.memoryStore.set(`decision-${Date.now()}`, {
decision: decision.decision,
score: decision.score,
confidence: decision.confidence,
duration,
timestamp: new Date()
}, 'decisions');
}
}
async getHistoricalGateDecisions() {
return []; // Placeholder for historical data
}
async evaluateTrendCriterion(criterion, value) {
// Evaluate trend-based criteria (requires historical data)
return value >= criterion.threshold;
}
async calculateImpact(criterion, value, context) {
if (criterion.critical && value < criterion.threshold) {
return 'High impact - critical criterion not met';
}
else if (value < criterion.threshold) {
return 'Medium impact - quality standard not met';
}
else {
return 'Low impact - criterion satisfied';
}
}
async getHistoricalPerformance(environment) {
return 0.85; // Placeholder for historical performance data
}
async calculateChangeMagnitude(changes) {
return changes.reduce((sum, change) => sum + change.complexity, 0) / Math.max(1, changes.length * 10);
}
}
exports.QualityGateAgent = QualityGateAgent;
// ============================================================================
// Supporting Classes (Placeholder Implementations)
// ============================================================================
class DecisionEngine {
constructor() {
this.decisionCount = 0;
this.totalTime = 0;
this.lastConfidence = 0.8;
}
async initialize() { }
getDecisionCount() {
return this.decisionCount;
}
getAverageTime() {
return this.decisionCount > 0 ? this.totalTime / this.decisionCount : 0;
}
getLastConfidence() {
return this.lastConfidence;
}
async cleanup() { }
}
class ConsciousnessEngine {
async initialize() { }
async buildDecisionTree(params) {
return {
version: '1.0.0',
structure: 'binary',
adaptationLevel: params.adaptationLevel
};
}
async cleanup() { }
}
class PsychoSymbolicReasoner {
async initialize() { }
async reason(params) {
// Simulate reasoning
return {
reasoning: 'Complex quality state analysis completed',
confidence: Math.random() * 0.5 + 0.5,
recommendations: ['Consider additional testing']
};
}
async cleanup() { }
}
class RiskAnalyzer {
async initialize() { }
async cleanup() { }
}
//# sourceMappingURL=QualityGateAgent.js.map