mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
9b7920d1ac
- Migrated all 16 agents to AQE hooks (197→0 Claude Flow commands) - Simplified coordination format to protocol: aqe-hooks - 100-500x performance improvement, zero external dependencies - Updated documentation and CLI templates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1095 lines
29 KiB
Markdown
1095 lines
29 KiB
Markdown
# AQE Hooks - Practical Usage Examples
|
|
|
|
**Version**: 1.0.2
|
|
**Last Updated**: 2025-10-07
|
|
|
|
## Table of Contents
|
|
|
|
1. [Basic Agent with Lifecycle Hooks](#basic-agent-with-lifecycle-hooks)
|
|
2. [Advanced Verification with VerificationHookManager](#advanced-verification-with-verificationhookmanager)
|
|
3. [Memory Coordination Patterns](#memory-coordination-patterns)
|
|
4. [Event-Driven Coordination](#event-driven-coordination)
|
|
5. [Error Handling Patterns](#error-handling-patterns)
|
|
6. [Performance Optimization Tips](#performance-optimization-tips)
|
|
7. [Complete Agent Examples](#complete-agent-examples)
|
|
|
|
---
|
|
|
|
## Basic Agent with Lifecycle Hooks
|
|
|
|
### Simple Test Generator Agent
|
|
|
|
This example shows a basic agent using lifecycle hooks for test generation.
|
|
|
|
```typescript
|
|
import { BaseAgent, BaseAgentConfig } from '../agents/BaseAgent';
|
|
import { TaskAssignment, QETask } from '../types';
|
|
|
|
export class SimpleTestGeneratorAgent extends BaseAgent {
|
|
private testFramework: 'jest' | 'mocha' = 'jest';
|
|
|
|
constructor(config: BaseAgentConfig) {
|
|
super(config);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Lifecycle Hooks
|
|
// ============================================================================
|
|
|
|
protected async onPreInitialization(): Promise<void> {
|
|
// Validate test framework is available
|
|
const hasJest = await this.checkModule('jest');
|
|
if (!hasJest) {
|
|
throw new Error('Jest not installed');
|
|
}
|
|
|
|
this.logger.info('Test framework validated');
|
|
}
|
|
|
|
protected async onPostInitialization(): Promise<void> {
|
|
// Load test templates from memory
|
|
const templates = await this.retrieveMemory('test-templates');
|
|
if (templates) {
|
|
this.logger.info('Loaded test templates', { count: templates.length });
|
|
}
|
|
|
|
// Register with fleet
|
|
await this.storeSharedMemory('status', {
|
|
ready: true,
|
|
framework: this.testFramework
|
|
});
|
|
}
|
|
|
|
protected async onPreTask(data: { assignment: TaskAssignment }): Promise<void> {
|
|
const { assignment } = data;
|
|
|
|
// Load project context
|
|
const context = await this.retrieveSharedMemory('coverage-analyzer', 'coverage-gaps');
|
|
if (context) {
|
|
this.logger.info('Found coverage gaps to address', { gaps: context.gaps });
|
|
}
|
|
|
|
// Store task start
|
|
await this.storeMemory(`task-${assignment.id}-start`, {
|
|
timestamp: Date.now(),
|
|
context
|
|
});
|
|
}
|
|
|
|
protected async onPostTask(data: { assignment: TaskAssignment; result: any }): Promise<void> {
|
|
const { assignment, result } = data;
|
|
|
|
// Store test generation results
|
|
await this.storeSharedMemory('test-results', {
|
|
taskId: assignment.id,
|
|
testsGenerated: result.count,
|
|
coverage: result.coverage,
|
|
timestamp: Date.now()
|
|
});
|
|
|
|
// Emit completion event
|
|
this.emitEvent('test:generated', {
|
|
agentId: this.agentId,
|
|
count: result.count,
|
|
coverage: result.coverage
|
|
}, 'high');
|
|
|
|
this.logger.info('Test generation complete', { result });
|
|
}
|
|
|
|
protected async onTaskError(data: { assignment: TaskAssignment; error: Error }): Promise<void> {
|
|
const { assignment, error } = data;
|
|
|
|
// Store error details
|
|
await this.storeMemory(`task-${assignment.id}-error`, {
|
|
error: error.message,
|
|
stack: error.stack,
|
|
timestamp: Date.now()
|
|
});
|
|
|
|
// Notify fleet
|
|
this.emitEvent('test:generation-failed', {
|
|
agentId: this.agentId,
|
|
taskId: assignment.id,
|
|
error: error.message
|
|
}, 'critical');
|
|
|
|
this.logger.error('Test generation failed', { error });
|
|
}
|
|
|
|
// ============================================================================
|
|
// Abstract Method Implementations
|
|
// ============================================================================
|
|
|
|
protected async initializeComponents(): Promise<void> {
|
|
// Load test framework configuration
|
|
this.testFramework = 'jest';
|
|
}
|
|
|
|
protected async performTask(task: QETask): Promise<any> {
|
|
// Generate tests based on task
|
|
const tests = await this.generateTests(task);
|
|
|
|
return {
|
|
count: tests.length,
|
|
coverage: 95,
|
|
tests
|
|
};
|
|
}
|
|
|
|
protected async loadKnowledge(): Promise<void> {
|
|
// Load test generation patterns
|
|
const patterns = await this.retrieveMemory('patterns');
|
|
if (patterns) {
|
|
this.logger.debug('Loaded test patterns', { count: patterns.length });
|
|
}
|
|
}
|
|
|
|
protected async cleanup(): Promise<void> {
|
|
// No cleanup needed for this agent
|
|
}
|
|
|
|
// ============================================================================
|
|
// Helper Methods
|
|
// ============================================================================
|
|
|
|
private async checkModule(moduleName: string): Promise<boolean> {
|
|
try {
|
|
require.resolve(moduleName);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private async generateTests(task: QETask): Promise<any[]> {
|
|
// Simplified test generation logic
|
|
return [
|
|
{ name: 'test 1', type: 'unit' },
|
|
{ name: 'test 2', type: 'integration' }
|
|
];
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Advanced Verification with VerificationHookManager
|
|
|
|
### Test Executor with Comprehensive Verification
|
|
|
|
This example shows advanced verification using VerificationHookManager.
|
|
|
|
```typescript
|
|
import { BaseAgent, BaseAgentConfig } from '../agents/BaseAgent';
|
|
import { VerificationHookManager } from '../core/hooks/VerificationHookManager';
|
|
import { TaskAssignment, QETask } from '../types';
|
|
|
|
export class TestExecutorAgent extends BaseAgent {
|
|
private hookManager: VerificationHookManager;
|
|
|
|
constructor(config: BaseAgentConfig) {
|
|
super(config);
|
|
this.hookManager = new VerificationHookManager(this.memoryStore);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Lifecycle Hooks with Advanced Verification
|
|
// ============================================================================
|
|
|
|
protected async onPreTask(data: { assignment: TaskAssignment }): Promise<void> {
|
|
const { assignment } = data;
|
|
|
|
// Stage 1: Pre-Task Verification (Priority 100)
|
|
const verification = await this.hookManager.executePreTaskVerification({
|
|
task: assignment.task.type,
|
|
context: {
|
|
// Environment checks
|
|
requiredVars: ['NODE_ENV', 'TEST_DATABASE_URL'],
|
|
minNodeVersion: '18.0.0',
|
|
requiredModules: ['jest', '@types/jest', 'ts-jest'],
|
|
|
|
// Resource checks
|
|
minMemoryMB: 1024,
|
|
minCPUCores: 2,
|
|
minDiskSpaceMB: 500,
|
|
maxLoadAverage: 4.0,
|
|
|
|
// Permission checks
|
|
files: ['package.json', 'jest.config.js', 'tsconfig.json'],
|
|
directories: ['tests', 'src', 'dist'],
|
|
requiredPermissions: ['read', 'write', 'execute'],
|
|
|
|
// Configuration checks
|
|
config: {
|
|
testEnvironment: 'node',
|
|
coverageThreshold: 90
|
|
},
|
|
requiredKeys: ['testEnvironment', 'coverageThreshold']
|
|
}
|
|
});
|
|
|
|
if (!verification.passed) {
|
|
throw new Error(
|
|
`Pre-task verification failed (score: ${verification.score}):\n` +
|
|
verification.checks.join('\n')
|
|
);
|
|
}
|
|
|
|
this.logger.info('Pre-task verification passed', {
|
|
score: verification.score,
|
|
priority: verification.priority,
|
|
checks: verification.checks
|
|
});
|
|
|
|
// Build context bundle for execution
|
|
const bundle = await this.hookManager.buildPreToolUseBundle({
|
|
task: assignment.task.type,
|
|
maxArtifacts: 10
|
|
});
|
|
|
|
// Store bundle for use during execution
|
|
await this.storeMemory(`task-${assignment.id}-bundle`, bundle);
|
|
}
|
|
|
|
protected async onPostTask(data: { assignment: TaskAssignment; result: any }): Promise<void> {
|
|
const { assignment, result } = data;
|
|
|
|
// Stage 2: Post-Task Validation (Priority 90)
|
|
const validation = await this.hookManager.executePostTaskValidation({
|
|
task: assignment.task.type,
|
|
result: {
|
|
// Output validation
|
|
output: result,
|
|
expectedStructure: {
|
|
tests: Array,
|
|
passed: Number,
|
|
failed: Number,
|
|
coverage: Object
|
|
},
|
|
expectedTypes: {
|
|
tests: 'array',
|
|
passed: 'number',
|
|
failed: 'number',
|
|
coverage: 'object'
|
|
},
|
|
requiredFields: ['tests', 'passed', 'failed', 'coverage'],
|
|
|
|
// Quality validation
|
|
metrics: {
|
|
complexity: result.metrics?.complexity || 0,
|
|
maintainability: result.metrics?.maintainability || 100,
|
|
duplication: result.metrics?.duplication || 0
|
|
},
|
|
qualityThresholds: {
|
|
maxComplexity: 10,
|
|
minMaintainability: 70,
|
|
maxDuplication: 10
|
|
},
|
|
|
|
// Coverage validation
|
|
coverage: result.coverage,
|
|
coverageThresholds: {
|
|
lines: 90,
|
|
branches: 85,
|
|
functions: 90,
|
|
statements: 90
|
|
},
|
|
coverageBaseline: await this.getCoverageBaseline(),
|
|
|
|
// Performance validation
|
|
performance: {
|
|
executionTime: result.executionTime,
|
|
memoryUsage: result.memoryUsage
|
|
},
|
|
performanceThresholds: {
|
|
executionTime: 60000, // 1 minute max
|
|
memoryUsage: 512 // 512 MB max
|
|
},
|
|
performanceBaseline: await this.getPerformanceBaseline(),
|
|
regressionThreshold: 0.1 // 10% regression tolerance
|
|
}
|
|
});
|
|
|
|
if (!validation.valid) {
|
|
this.logger.warn('Post-task validation issues', {
|
|
accuracy: validation.accuracy,
|
|
priority: validation.priority,
|
|
validations: validation.validations
|
|
});
|
|
} else {
|
|
this.logger.info('Post-task validation passed', {
|
|
accuracy: validation.accuracy
|
|
});
|
|
}
|
|
|
|
// Persist outcomes
|
|
await this.hookManager.persistPostToolUseOutcomes({
|
|
events: [
|
|
{
|
|
type: 'test:executed',
|
|
payload: {
|
|
tests: result.tests.length,
|
|
passed: result.passed,
|
|
failed: result.failed
|
|
}
|
|
}
|
|
],
|
|
patterns: [
|
|
{
|
|
pattern: 'test-execution-success',
|
|
confidence: validation.accuracy
|
|
}
|
|
],
|
|
checkpoints: [
|
|
{
|
|
step: 'execution',
|
|
status: validation.valid ? 'completed' : 'completed-with-warnings'
|
|
}
|
|
],
|
|
artifacts: [
|
|
{
|
|
kind: 'test-results',
|
|
path: result.outputPath,
|
|
sha256: result.hash
|
|
}
|
|
],
|
|
metrics: [
|
|
{ metric: 'tests_executed', value: result.tests.length, unit: 'count' },
|
|
{ metric: 'tests_passed', value: result.passed, unit: 'count' },
|
|
{ metric: 'tests_failed', value: result.failed, unit: 'count' },
|
|
{ metric: 'coverage_lines', value: result.coverage.lines, unit: 'percent' },
|
|
{ metric: 'execution_time', value: result.executionTime, unit: 'ms' }
|
|
]
|
|
});
|
|
|
|
// Store results in memory
|
|
await this.storeSharedMemory('test-execution-results', {
|
|
taskId: assignment.id,
|
|
result,
|
|
validation,
|
|
timestamp: Date.now()
|
|
});
|
|
}
|
|
|
|
// ============================================================================
|
|
// Abstract Method Implementations
|
|
// ============================================================================
|
|
|
|
protected async initializeComponents(): Promise<void> {
|
|
// Initialize test runner
|
|
}
|
|
|
|
protected async performTask(task: QETask): Promise<any> {
|
|
// Load context bundle
|
|
const bundle = await this.retrieveMemory(`task-${task.id}-bundle`);
|
|
|
|
// Execute tests
|
|
const startTime = Date.now();
|
|
const result = await this.executeTests(task, bundle);
|
|
const executionTime = Date.now() - startTime;
|
|
|
|
return {
|
|
...result,
|
|
executionTime,
|
|
memoryUsage: process.memoryUsage().heapUsed / 1024 / 1024 // MB
|
|
};
|
|
}
|
|
|
|
protected async loadKnowledge(): Promise<void> {
|
|
// Load test execution patterns
|
|
}
|
|
|
|
protected async cleanup(): Promise<void> {
|
|
// Cleanup test artifacts
|
|
}
|
|
|
|
// ============================================================================
|
|
// Helper Methods
|
|
// ============================================================================
|
|
|
|
private async getCoverageBaseline(): Promise<any> {
|
|
const baseline = await this.retrieveMemory('coverage-baseline');
|
|
return baseline || {
|
|
lines: 85,
|
|
branches: 80,
|
|
functions: 85,
|
|
statements: 85
|
|
};
|
|
}
|
|
|
|
private async getPerformanceBaseline(): Promise<any> {
|
|
const baseline = await this.retrieveMemory('performance-baseline');
|
|
return baseline || {
|
|
executionTime: 45000,
|
|
memoryUsage: 400
|
|
};
|
|
}
|
|
|
|
private async executeTests(task: QETask, bundle: any): Promise<any> {
|
|
// Simplified test execution
|
|
return {
|
|
tests: [
|
|
{ name: 'test 1', status: 'passed' },
|
|
{ name: 'test 2', status: 'passed' }
|
|
],
|
|
passed: 2,
|
|
failed: 0,
|
|
coverage: {
|
|
lines: 95,
|
|
branches: 90,
|
|
functions: 92,
|
|
statements: 94
|
|
},
|
|
metrics: {
|
|
complexity: 8,
|
|
maintainability: 75,
|
|
duplication: 5
|
|
},
|
|
outputPath: 'test-results.json',
|
|
hash: 'abc123...'
|
|
};
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Memory Coordination Patterns
|
|
|
|
### Producer-Consumer Pattern
|
|
|
|
```typescript
|
|
// Producer Agent (Test Generator)
|
|
export class TestGeneratorAgent extends BaseAgent {
|
|
protected async onPostTask(data: any): Promise<void> {
|
|
const { result } = data;
|
|
|
|
// Store generated tests for executor
|
|
await this.memoryStore.store('generated-tests', result.tests, {
|
|
partition: 'test_queue',
|
|
ttl: 3600, // 1 hour
|
|
accessLevel: AccessLevel.SWARM
|
|
});
|
|
|
|
// Emit event
|
|
this.eventBus.emit('tests:ready', {
|
|
count: result.tests.length
|
|
});
|
|
}
|
|
}
|
|
|
|
// Consumer Agent (Test Executor)
|
|
export class TestExecutorAgent extends BaseAgent {
|
|
constructor(config: BaseAgentConfig) {
|
|
super(config);
|
|
|
|
// Listen for tests ready event
|
|
this.registerEventHandler({
|
|
eventType: 'tests:ready',
|
|
handler: async (event) => {
|
|
// Retrieve tests from memory
|
|
const tests = await this.memoryStore.retrieve('generated-tests', {
|
|
partition: 'test_queue'
|
|
});
|
|
|
|
if (tests) {
|
|
await this.executeTests(tests);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private async executeTests(tests: any[]): Promise<void> {
|
|
// Execute tests...
|
|
}
|
|
}
|
|
```
|
|
|
|
### Coordinator-Worker Pattern
|
|
|
|
```typescript
|
|
// Coordinator Agent
|
|
export class FleetCoordinatorAgent extends BaseAgent {
|
|
protected async performTask(task: QETask): Promise<any> {
|
|
// Store coordination plan
|
|
await this.memoryStore.store('coordination-plan', {
|
|
phase: 'test-generation',
|
|
requiredAgents: ['test-generator', 'test-executor', 'coverage-analyzer']
|
|
}, {
|
|
partition: 'coordination',
|
|
accessLevel: AccessLevel.SWARM
|
|
});
|
|
|
|
// Broadcast to workers
|
|
this.eventBus.emit('coordination:plan-ready', {
|
|
phase: 'test-generation'
|
|
});
|
|
|
|
// Wait for workers to complete
|
|
const results = await this.waitForWorkers();
|
|
|
|
return results;
|
|
}
|
|
|
|
private async waitForWorkers(): Promise<any> {
|
|
return new Promise((resolve) => {
|
|
const results: any[] = [];
|
|
|
|
this.registerEventHandler({
|
|
eventType: 'worker:completed',
|
|
handler: async (event) => {
|
|
results.push(event.data);
|
|
|
|
if (results.length === 3) {
|
|
resolve(results);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// Worker Agent
|
|
export class WorkerAgent extends BaseAgent {
|
|
constructor(config: BaseAgentConfig) {
|
|
super(config);
|
|
|
|
this.registerEventHandler({
|
|
eventType: 'coordination:plan-ready',
|
|
handler: async (event) => {
|
|
// Retrieve plan
|
|
const plan = await this.memoryStore.retrieve('coordination-plan', {
|
|
partition: 'coordination'
|
|
});
|
|
|
|
// Execute assigned work
|
|
const result = await this.performWork(plan);
|
|
|
|
// Report completion
|
|
this.eventBus.emit('worker:completed', {
|
|
agentId: this.agentId.id,
|
|
result
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
private async performWork(plan: any): Promise<any> {
|
|
// Perform work...
|
|
return { success: true };
|
|
}
|
|
}
|
|
```
|
|
|
|
### Shared State Pattern
|
|
|
|
```typescript
|
|
export class SharedStateAgent extends BaseAgent {
|
|
protected async onPreTask(data: any): Promise<void> {
|
|
// Read shared state
|
|
const state = await this.memoryStore.retrieve('fleet-state', {
|
|
partition: 'shared_state',
|
|
isSystemAgent: true // Bypass access control
|
|
});
|
|
|
|
this.logger.info('Current fleet state', { state });
|
|
}
|
|
|
|
protected async onPostTask(data: any): Promise<void> {
|
|
// Update shared state
|
|
await this.memoryStore.store('fleet-state', {
|
|
phase: 'testing',
|
|
progress: 75,
|
|
updatedBy: this.agentId.id,
|
|
timestamp: Date.now()
|
|
}, {
|
|
partition: 'shared_state',
|
|
accessLevel: AccessLevel.PUBLIC
|
|
});
|
|
|
|
// Broadcast state change
|
|
this.eventBus.emit('fleet:state-changed', {
|
|
phase: 'testing',
|
|
progress: 75
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Event-Driven Coordination
|
|
|
|
### Event Priority Handling
|
|
|
|
```typescript
|
|
export class PriorityHandlerAgent extends BaseAgent {
|
|
constructor(config: BaseAgentConfig) {
|
|
super(config);
|
|
|
|
// Critical events (processed first)
|
|
this.registerEventHandler({
|
|
eventType: 'error:critical',
|
|
handler: async (event) => {
|
|
this.logger.error('Critical error detected', { event });
|
|
await this.handleCriticalError(event.data);
|
|
}
|
|
});
|
|
|
|
// High priority events
|
|
this.registerEventHandler({
|
|
eventType: 'test:failed',
|
|
handler: async (event) => {
|
|
this.logger.warn('Test failed', { event });
|
|
await this.handleTestFailure(event.data);
|
|
}
|
|
});
|
|
|
|
// Medium priority events
|
|
this.registerEventHandler({
|
|
eventType: 'test:completed',
|
|
handler: async (event) => {
|
|
this.logger.info('Test completed', { event });
|
|
await this.handleTestCompletion(event.data);
|
|
}
|
|
});
|
|
|
|
// Low priority events
|
|
this.registerEventHandler({
|
|
eventType: 'status:update',
|
|
handler: async (event) => {
|
|
this.logger.debug('Status update', { event });
|
|
await this.handleStatusUpdate(event.data);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected async performTask(task: QETask): Promise<any> {
|
|
try {
|
|
const result = await this.executeTask(task);
|
|
|
|
// Emit with appropriate priority
|
|
this.emitEvent('test:completed', result, 'medium');
|
|
|
|
return result;
|
|
} catch (error) {
|
|
// Emit critical error
|
|
this.emitEvent('error:critical', {
|
|
error: error.message,
|
|
taskId: task.id
|
|
}, 'critical');
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
private async handleCriticalError(data: any): Promise<void> {
|
|
// Immediate action required
|
|
}
|
|
|
|
private async handleTestFailure(data: any): Promise<void> {
|
|
// High priority action
|
|
}
|
|
|
|
private async handleTestCompletion(data: any): Promise<void> {
|
|
// Normal processing
|
|
}
|
|
|
|
private async handleStatusUpdate(data: any): Promise<void> {
|
|
// Background processing
|
|
}
|
|
|
|
private async executeTask(task: QETask): Promise<any> {
|
|
return { success: true };
|
|
}
|
|
}
|
|
```
|
|
|
|
### Event Filtering and Routing
|
|
|
|
```typescript
|
|
export class EventRouterAgent extends BaseAgent {
|
|
constructor(config: BaseAgentConfig) {
|
|
super(config);
|
|
|
|
// Filter events by source
|
|
this.registerEventHandler({
|
|
eventType: 'test:completed',
|
|
handler: async (event) => {
|
|
// Only process events from test-executor agents
|
|
if (event.source.type === 'test-executor') {
|
|
await this.processTestCompletion(event.data);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Filter events by data
|
|
this.registerEventHandler({
|
|
eventType: 'coverage:updated',
|
|
handler: async (event) => {
|
|
// Only process if coverage is below threshold
|
|
if (event.data.coverage < 90) {
|
|
await this.handleLowCoverage(event.data);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Compound event handling
|
|
this.registerEventHandler({
|
|
eventType: 'test:completed',
|
|
handler: async (event) => {
|
|
// Wait for multiple events before processing
|
|
await this.aggregateEvents(event);
|
|
}
|
|
});
|
|
}
|
|
|
|
private async processTestCompletion(data: any): Promise<void> {
|
|
// Process completion
|
|
}
|
|
|
|
private async handleLowCoverage(data: any): Promise<void> {
|
|
// Handle low coverage
|
|
}
|
|
|
|
private eventBuffer: any[] = [];
|
|
|
|
private async aggregateEvents(event: any): Promise<void> {
|
|
this.eventBuffer.push(event);
|
|
|
|
// Process when we have 5 events
|
|
if (this.eventBuffer.length >= 5) {
|
|
await this.processBatch(this.eventBuffer);
|
|
this.eventBuffer = [];
|
|
}
|
|
}
|
|
|
|
private async processBatch(events: any[]): Promise<void> {
|
|
// Batch processing
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Error Handling Patterns
|
|
|
|
### Graceful Degradation
|
|
|
|
```typescript
|
|
export class ResilientAgent extends BaseAgent {
|
|
protected async onTaskError(data: any): Promise<void> {
|
|
const { assignment, error } = data;
|
|
|
|
// Attempt recovery based on error type
|
|
if (error.message.includes('ECONNREFUSED')) {
|
|
await this.handleConnectionError(assignment);
|
|
} else if (error.message.includes('ETIMEDOUT')) {
|
|
await this.handleTimeoutError(assignment);
|
|
} else if (error.message.includes('ENOMEM')) {
|
|
await this.handleMemoryError(assignment);
|
|
} else {
|
|
await this.handleUnknownError(assignment, error);
|
|
}
|
|
}
|
|
|
|
private async handleConnectionError(assignment: any): Promise<void> {
|
|
// Retry with backoff
|
|
let retries = 0;
|
|
const maxRetries = 3;
|
|
|
|
while (retries < maxRetries) {
|
|
try {
|
|
await this.sleep(Math.pow(2, retries) * 1000);
|
|
const result = await this.performTask(assignment.task);
|
|
|
|
// Success - store result
|
|
await this.storeMemory(`task-${assignment.id}-result`, result);
|
|
return;
|
|
} catch (error) {
|
|
retries++;
|
|
this.logger.warn(`Retry ${retries}/${maxRetries}`, { error });
|
|
}
|
|
}
|
|
|
|
// All retries failed - fallback
|
|
await this.fallbackStrategy(assignment);
|
|
}
|
|
|
|
private async handleTimeoutError(assignment: any): Promise<void> {
|
|
// Increase timeout and retry
|
|
this.context.timeout = (this.context.timeout || 30000) * 2;
|
|
await this.performTask(assignment.task);
|
|
}
|
|
|
|
private async handleMemoryError(assignment: any): Promise<void> {
|
|
// Trigger garbage collection
|
|
if (global.gc) {
|
|
global.gc();
|
|
}
|
|
|
|
// Reduce batch size
|
|
this.context.batchSize = Math.floor((this.context.batchSize || 10) / 2);
|
|
|
|
// Retry with smaller batch
|
|
await this.performTask(assignment.task);
|
|
}
|
|
|
|
private async handleUnknownError(assignment: any, error: Error): Promise<void> {
|
|
// Store error for analysis
|
|
await this.memoryStore.store(`errors/${assignment.id}`, {
|
|
error: error.message,
|
|
stack: error.stack,
|
|
context: this.context
|
|
}, {
|
|
partition: 'errors',
|
|
ttl: 604800
|
|
});
|
|
|
|
// Emit critical error event
|
|
this.emitEvent('error:unknown', {
|
|
agentId: this.agentId,
|
|
error: error.message
|
|
}, 'critical');
|
|
}
|
|
|
|
private async fallbackStrategy(assignment: any): Promise<void> {
|
|
// Use cached results or default behavior
|
|
this.logger.warn('Using fallback strategy', { taskId: assignment.id });
|
|
}
|
|
|
|
private sleep(ms: number): Promise<void> {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
}
|
|
```
|
|
|
|
### Circuit Breaker Pattern
|
|
|
|
```typescript
|
|
export class CircuitBreakerAgent extends BaseAgent {
|
|
private circuitState: 'closed' | 'open' | 'half-open' = 'closed';
|
|
private failureCount = 0;
|
|
private lastFailureTime = 0;
|
|
private readonly failureThreshold = 5;
|
|
private readonly resetTimeout = 60000; // 1 minute
|
|
|
|
protected async performTask(task: QETask): Promise<any> {
|
|
// Check circuit state
|
|
if (this.circuitState === 'open') {
|
|
const timeSinceFailure = Date.now() - this.lastFailureTime;
|
|
|
|
if (timeSinceFailure >= this.resetTimeout) {
|
|
// Try half-open
|
|
this.circuitState = 'half-open';
|
|
this.logger.info('Circuit breaker half-open');
|
|
} else {
|
|
throw new Error('Circuit breaker open - service unavailable');
|
|
}
|
|
}
|
|
|
|
try {
|
|
const result = await this.executeWithCircuitBreaker(task);
|
|
|
|
// Success - reset circuit
|
|
if (this.circuitState === 'half-open') {
|
|
this.circuitState = 'closed';
|
|
this.failureCount = 0;
|
|
this.logger.info('Circuit breaker closed');
|
|
}
|
|
|
|
return result;
|
|
} catch (error) {
|
|
this.handleCircuitBreakerFailure();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
private async executeWithCircuitBreaker(task: QETask): Promise<any> {
|
|
// Actual task execution
|
|
return { success: true };
|
|
}
|
|
|
|
private handleCircuitBreakerFailure(): void {
|
|
this.failureCount++;
|
|
this.lastFailureTime = Date.now();
|
|
|
|
if (this.failureCount >= this.failureThreshold) {
|
|
this.circuitState = 'open';
|
|
this.logger.error('Circuit breaker opened', {
|
|
failureCount: this.failureCount
|
|
});
|
|
|
|
// Store circuit state
|
|
this.storeMemory('circuit-state', {
|
|
state: this.circuitState,
|
|
failureCount: this.failureCount,
|
|
timestamp: this.lastFailureTime
|
|
});
|
|
|
|
// Emit event
|
|
this.emitEvent('circuit:opened', {
|
|
agentId: this.agentId,
|
|
failureCount: this.failureCount
|
|
}, 'critical');
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Optimization Tips
|
|
|
|
### Batch Operations
|
|
|
|
```typescript
|
|
export class BatchProcessorAgent extends BaseAgent {
|
|
private batchSize = 10;
|
|
private taskQueue: QETask[] = [];
|
|
|
|
protected async performTask(task: QETask): Promise<any> {
|
|
// Add to queue
|
|
this.taskQueue.push(task);
|
|
|
|
// Process when batch is full
|
|
if (this.taskQueue.length >= this.batchSize) {
|
|
return await this.processBatch();
|
|
}
|
|
|
|
return { queued: true };
|
|
}
|
|
|
|
private async processBatch(): Promise<any> {
|
|
const batch = this.taskQueue.splice(0, this.batchSize);
|
|
|
|
// Process all tasks in parallel
|
|
const results = await Promise.all(
|
|
batch.map(task => this.processTask(task))
|
|
);
|
|
|
|
// Store batch results
|
|
await this.memoryStore.store(`batch-${Date.now()}`, results, {
|
|
partition: 'batch_results',
|
|
ttl: 3600
|
|
});
|
|
|
|
return { processed: results.length };
|
|
}
|
|
|
|
private async processTask(task: QETask): Promise<any> {
|
|
// Individual task processing
|
|
return { taskId: task.id, success: true };
|
|
}
|
|
}
|
|
```
|
|
|
|
### Caching with Memory Store
|
|
|
|
```typescript
|
|
export class CachingAgent extends BaseAgent {
|
|
protected async performTask(task: QETask): Promise<any> {
|
|
const cacheKey = `cache:${task.type}:${task.id}`;
|
|
|
|
// Check cache first
|
|
const cached = await this.memoryStore.retrieve(cacheKey, {
|
|
partition: 'cache'
|
|
});
|
|
|
|
if (cached) {
|
|
this.logger.debug('Cache hit', { taskId: task.id });
|
|
return cached;
|
|
}
|
|
|
|
// Cache miss - compute result
|
|
this.logger.debug('Cache miss', { taskId: task.id });
|
|
const result = await this.computeResult(task);
|
|
|
|
// Store in cache with TTL
|
|
await this.memoryStore.store(cacheKey, result, {
|
|
partition: 'cache',
|
|
ttl: 3600 // 1 hour
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
private async computeResult(task: QETask): Promise<any> {
|
|
// Expensive computation
|
|
return { taskId: task.id, computed: true };
|
|
}
|
|
}
|
|
```
|
|
|
|
### Parallel Execution
|
|
|
|
```typescript
|
|
export class ParallelExecutorAgent extends BaseAgent {
|
|
protected async performTask(task: QETask): Promise<any> {
|
|
// Split task into subtasks
|
|
const subtasks = this.splitTask(task);
|
|
|
|
// Execute all subtasks in parallel
|
|
const results = await Promise.all(
|
|
subtasks.map(subtask => this.executeSubtask(subtask))
|
|
);
|
|
|
|
// Aggregate results
|
|
return this.aggregateResults(results);
|
|
}
|
|
|
|
private splitTask(task: QETask): QETask[] {
|
|
// Split logic
|
|
return [task, task, task]; // Example: 3 subtasks
|
|
}
|
|
|
|
private async executeSubtask(subtask: QETask): Promise<any> {
|
|
// Subtask execution
|
|
return { subtaskId: subtask.id, success: true };
|
|
}
|
|
|
|
private aggregateResults(results: any[]): any {
|
|
return {
|
|
totalSubtasks: results.length,
|
|
success: results.every(r => r.success)
|
|
};
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Complete Agent Examples
|
|
|
|
### Test Generator Agent (Full Implementation)
|
|
|
|
See [AQE-HOOKS-GUIDE.md](../AQE-HOOKS-GUIDE.md) for complete examples of:
|
|
- Test Generator Agent with lifecycle hooks
|
|
- Test Executor Agent with VerificationHookManager
|
|
- Coverage Analyzer Agent with memory coordination
|
|
- Quality Gate Agent with event-driven coordination
|
|
|
|
### QE Agent Types
|
|
|
|
All QE agents follow these patterns:
|
|
|
|
1. **qe-test-generator** - Uses lifecycle hooks for test generation
|
|
2. **qe-test-executor** - Uses VerificationHookManager for execution
|
|
3. **qe-coverage-analyzer** - Uses memory coordination patterns
|
|
4. **qe-quality-gate** - Uses event-driven coordination
|
|
5. **qe-performance-tester** - Uses batch processing and caching
|
|
6. **qe-security-scanner** - Uses circuit breaker pattern
|
|
7. **qe-fleet-commander** - Uses coordinator-worker pattern
|
|
|
|
---
|
|
|
|
**Next Steps**:
|
|
- Read [AQE Hooks Guide](../AQE-HOOKS-GUIDE.md) for comprehensive documentation
|
|
- Explore [Hooks API Reference](../../src/core/hooks/README.md)
|
|
- Study [Migration Plan](../HOOKS-MIGRATION-PLAN.md) for transition details
|
|
|
|
**Version**: 1.0.2
|
|
**Last Updated**: 2025-10-08
|
|
**Maintained By**: Agentic QE Fleet Team
|