Files
proffesor-for-testing__agen…/tests/learning/coherence-integration.test.ts
Profa 2253d05618 feat: flatten project structure — archive v2, promote v3 to root
Remove the nested v3/ subdirectory by moving all active source code,
tests, configs, and assets directly to the project root. Archive v2/
(dead code, 847MB) to v2-archive.tar.gz.

Key changes:
- Move v3/src/ → src/, v3/tests/ → tests/, v3/packages/ → packages/
- Move v3/tsconfig.json, v3/vitest.config.ts to root
- Merge v3/package.json into root package.json (ESM, scripts, deps, exports)
- Merge v3/scripts/ into scripts/, v3/implementation/ into docs/implementation/
- Update all CI workflows to remove "cd v3 &&" and v3/ path prefixes
- Fix all init installer path resolution (3-level-up → 2-level-up)
- Fix hooks to use "node ./dist/cli/bundle.js" instead of bare "aqe"
- Update settings.json ADR/DDD directories to docs/implementation/
- Fix helper scripts (statusline, adr-compliance, ddd-tracker, guidance-hooks)
- Update 58 agent definitions: aqe/v3/ → aqe/ memory namespaces
- Update 15 guidance shards: v3/src/ → src/ path references
- Fix sync interfaces: remove v3/ and v2/ path references
- Fix test path calculations (process.cwd() + '..' no longer needed)
- Fix flaky perf test threshold (500ms → 1000ms for CI environments)
- Fix complexity-analyzer test to match ADR-051 keyword-based eligibility

Build passes, 17,901 tests pass, CLI and MCP bundles verified.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:35:41 +00:00

781 lines
27 KiB
TypeScript

/**
* Learning Module Coherence Integration Tests
* ADR-052 Phase 3 Action A3.5
*
* Comprehensive integration tests for coherence filtering in the learning module.
* Tests cover:
* - Pattern retrieval with coherence filtering
* - Memory coherence auditing
* - Promotion coherence gates
* - Causal verification
*
* These tests use REAL components where available, with mocks for expensive WASM operations.
*/
import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';
import { QEReasoningBank, createQEReasoningBank } from '../../src/learning/qe-reasoning-bank.js';
import { RealQEReasoningBank, createRealQEReasoningBank } from '../../src/learning/real-qe-reasoning-bank.js';
import { CoherenceService, createCoherenceService } from '../../src/integrations/coherence/index.js';
import { WasmLoader } from '../../src/integrations/coherence/wasm-loader.js';
import { InMemoryBackend } from '../../src/kernel/memory-backend.js';
import { InMemoryEventBus } from '../../src/kernel/event-bus.js';
import type { CreateQEPatternOptions } from '../../src/learning/qe-patterns.js';
import type { EventBus } from '../../src/kernel/interfaces.js';
import type { CoherenceNode, CoherenceResult } from '../../src/integrations/coherence/types.js';
// ============================================================================
// Test Helpers
// ============================================================================
/**
* Helper to create test patterns with customizable properties
*/
function createTestPattern(overrides: Partial<CreateQEPatternOptions> = {}): CreateQEPatternOptions {
const timestamp = Date.now();
return {
patternType: 'test-template',
name: `test-pattern-${timestamp}`,
description: 'Test pattern for integration testing',
template: {
type: 'code',
content: 'test content',
variables: [],
},
context: { tags: ['test'], testType: 'unit' },
...overrides,
};
}
/**
* Helper to wait for events with timeout
*/
function waitForEvent(eventBus: EventBus, eventName: string, timeout = 5000): Promise<unknown> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error(`Timeout waiting for ${eventName}`)), timeout);
const subscription = eventBus.subscribe(eventName, (data) => {
clearTimeout(timer);
subscription.unsubscribe();
resolve(data);
});
});
}
/**
* Helper to create a mock WASM loader that simulates coherence checks
*/
function createMockWasmLoader() {
const wasmLoader = {
isAvailable: vi.fn().mockResolvedValue(false), // Use fallback by default
getEngines: vi.fn().mockRejectedValue(new Error('WASM not available in tests')),
on: vi.fn(),
off: vi.fn(),
getState: vi.fn().mockReturnValue('unloaded'),
getLastError: vi.fn().mockReturnValue(null),
};
return wasmLoader;
}
/**
* Helper to create coherence nodes from pattern search results
*/
function createCoherenceNodes(patterns: Array<{ id: string; embedding: number[]; name: string }>): CoherenceNode[] {
return patterns.map(p => ({
id: p.id,
embedding: p.embedding,
weight: 1.0,
metadata: { name: p.name },
}));
}
// ============================================================================
// Test Suite Setup
// ============================================================================
describe('Learning Module Coherence Integration', () => {
let memory: InMemoryBackend;
let eventBus: InMemoryEventBus;
let wasmLoader: ReturnType<typeof createMockWasmLoader>;
let coherenceService: CoherenceService;
beforeAll(async () => {
memory = new InMemoryBackend();
await memory.initialize();
eventBus = new InMemoryEventBus();
wasmLoader = createMockWasmLoader();
coherenceService = new CoherenceService(wasmLoader as any, {
fallbackEnabled: true,
coherenceThreshold: 0.4,
});
await coherenceService.initialize();
});
afterAll(async () => {
await coherenceService.dispose();
await memory.dispose();
await eventBus.dispose();
});
beforeEach(async () => {
await memory.clear();
});
// ============================================================================
// A. Pattern Retrieval Coherence Filtering
// ============================================================================
describe('Pattern Retrieval Coherence Filtering', () => {
it('should filter incoherent patterns from retrieval', async () => {
const bank = createQEReasoningBank(memory, eventBus);
await bank.initialize();
// Store patterns that would be incoherent together
// Pattern 1: Unit testing pattern (coherent)
const pattern1 = await bank.storePattern(createTestPattern({
name: 'AAA Unit Test Pattern',
description: 'Arrange-Act-Assert unit testing pattern',
context: { tags: ['unit-test', 'aaa'], testType: 'unit' },
}));
expect(pattern1.success).toBe(true);
// Pattern 2: Integration testing pattern (coherent with unit tests)
const pattern2 = await bank.storePattern(createTestPattern({
name: 'Integration Test Pattern',
description: 'End-to-end integration testing pattern',
context: { tags: ['integration-test', 'e2e'], testType: 'integration' },
}));
expect(pattern2.success).toBe(true);
// Pattern 3: Contradictory pattern (suggests skipping tests)
const pattern3 = await bank.storePattern(createTestPattern({
name: 'Skip Tests Pattern',
description: 'Pattern that suggests skipping test execution',
context: { tags: ['skip', 'no-test'], testType: 'unit' },
}));
expect(pattern3.success).toBe(true);
// Search for test patterns
const searchResult = await bank.searchPatterns('unit testing best practices', {
limit: 10,
});
expect(searchResult.success).toBe(true);
if (!searchResult.success) return;
const patterns = searchResult.value;
expect(patterns.length).toBeGreaterThan(0);
// Check coherence of retrieved patterns
const nodes: CoherenceNode[] = patterns.map(p => ({
id: p.pattern.id,
embedding: p.pattern.embedding,
weight: p.similarity,
metadata: { name: p.pattern.name },
}));
const coherenceCheck = await coherenceService.checkCoherence(nodes);
// Verify that coherence filtering would identify conflicts
// (In a full implementation, the bank would filter before returning)
if (coherenceCheck.contradictions.length > 0) {
expect(coherenceCheck.isCoherent).toBe(false);
expect(coherenceCheck.contradictions.some(c => c.severity === 'high' || c.severity === 'critical')).toBe(true);
}
await bank.dispose();
}, 10000);
it('should return all patterns when coherence service unavailable', async () => {
const bank = createQEReasoningBank(memory, eventBus);
await bank.initialize();
// Store multiple patterns
await bank.storePattern(createTestPattern({ name: 'Pattern A' }));
await bank.storePattern(createTestPattern({ name: 'Pattern B' }));
await bank.storePattern(createTestPattern({ name: 'Pattern C' }));
// Search without coherence filtering (fallback behavior)
const result = await bank.searchPatterns('test pattern', { limit: 10 });
expect(result.success).toBe(true);
if (!result.success) return;
// Should return all matching patterns (no filtering when coherence unavailable)
expect(result.value.length).toBeGreaterThanOrEqual(3);
await bank.dispose();
});
it('should emit event when all patterns are incoherent', async () => {
const bank = createQEReasoningBank(memory, eventBus);
await bank.initialize();
// Set up event listener
const eventPromise = waitForEvent(eventBus, 'learning:coherence_warning');
// Store highly contradictory patterns
const contradictoryPatterns = [
createTestPattern({
name: 'Always Mock Dependencies',
description: 'Always use mocks for all dependencies',
context: { tags: ['mocking', 'isolation'] },
}),
createTestPattern({
name: 'Never Use Mocks',
description: 'Avoid mocks, use real dependencies',
context: { tags: ['integration', 'real-deps'] },
}),
];
for (const pattern of contradictoryPatterns) {
await bank.storePattern(pattern);
}
// Search - should trigger coherence check
const result = await bank.searchPatterns('dependency testing approach');
expect(result.success).toBe(true);
// In a full implementation, this would emit an event
// For now, we verify the patterns were stored
if (result.success) {
expect(result.value.length).toBeGreaterThan(0);
}
await bank.dispose();
// Note: Event emission would be implemented in real coherence-aware search
// This test documents the expected behavior
}, 7000);
});
// ============================================================================
// B. Memory Coherence Auditor
// ============================================================================
describe('Memory Coherence Auditor', () => {
it('should audit memory and report global coherence energy', async () => {
const bank = createQEReasoningBank(memory, eventBus);
await bank.initialize();
// Store multiple patterns across different domains
const patterns = [
createTestPattern({ name: 'Unit Test Pattern', context: { tags: ['unit'] } }),
createTestPattern({ name: 'Integration Test Pattern', context: { tags: ['integration'] } }),
createTestPattern({ name: 'E2E Test Pattern', context: { tags: ['e2e'] } }),
createTestPattern({ name: 'Security Test Pattern', context: { tags: ['security'] } }),
];
for (const pattern of patterns) {
await bank.storePattern(pattern);
}
// Get all patterns for audit
const allPatterns = await bank.searchPatterns('', { limit: 100 });
expect(allPatterns.success).toBe(true);
if (!allPatterns.success) return;
// Create coherence nodes
const nodes = allPatterns.value.map(p => ({
id: p.pattern.id,
embedding: p.pattern.embedding,
weight: 1.0,
metadata: { domain: p.pattern.qeDomain },
}));
// Run coherence audit
const auditResult = await coherenceService.checkCoherence(nodes);
// Verify energy is reported
expect(typeof auditResult.energy).toBe('number');
expect(auditResult.energy).toBeGreaterThanOrEqual(0);
expect(auditResult.durationMs).toBeGreaterThanOrEqual(0);
// Verify coherence assessment
expect(typeof auditResult.isCoherent).toBe('boolean');
expect(auditResult.lane).toMatch(/^(reflex|retrieval|heavy|human)$/);
await bank.dispose();
});
it('should identify hotspots in pattern clusters', async () => {
const bank = createQEReasoningBank(memory, eventBus);
await bank.initialize();
// Store conflicting patterns in same domain
const conflictingPatterns = [
createTestPattern({
name: 'Test First (TDD)',
description: 'Write tests before implementation',
context: { tags: ['tdd', 'test-first'], testType: 'unit' },
}),
createTestPattern({
name: 'Test Last',
description: 'Write tests after implementation',
context: { tags: ['test-last'], testType: 'unit' },
}),
createTestPattern({
name: 'No Tests Needed',
description: 'Some code does not need tests',
context: { tags: ['no-tests'], testType: 'unit' },
}),
];
for (const pattern of conflictingPatterns) {
await bank.storePattern(pattern);
}
// Search for test methodology patterns
const result = await bank.searchPatterns('test methodology approach');
expect(result.success).toBe(true);
if (!result.success) return;
// Check for contradictions (hotspots)
const nodes = result.value.map(p => ({
id: p.pattern.id,
embedding: p.pattern.embedding,
weight: p.similarity,
metadata: { name: p.pattern.name },
}));
const coherenceCheck = await coherenceService.checkCoherence(nodes);
// Should detect contradictions in the cluster
if (coherenceCheck.contradictions.length > 0) {
expect(coherenceCheck.contradictions).toBeDefined();
// Hotspots are indicated by high-severity contradictions
const highSeverity = coherenceCheck.contradictions.filter(
c => c.severity === 'high' || c.severity === 'critical'
);
expect(highSeverity.length).toBeGreaterThan(0);
}
await bank.dispose();
});
it('should generate recommendations for high-energy patterns', async () => {
const bank = createQEReasoningBank(memory, eventBus);
await bank.initialize();
// Store patterns that create high energy (low coherence)
const highEnergyPatterns = [
createTestPattern({
name: 'Mocking Strategy A',
description: 'Mock everything aggressively',
embedding: [1.0, 0.0, 0.0, 0.5],
}),
createTestPattern({
name: 'Mocking Strategy B',
description: 'Never mock, always use real dependencies',
embedding: [-1.0, 0.0, 0.0, -0.5], // Opposite embedding
}),
];
for (const pattern of highEnergyPatterns) {
await bank.storePattern(pattern);
}
const result = await bank.searchPatterns('mocking approach');
expect(result.success).toBe(true);
if (!result.success) return;
const nodes = result.value.map(p => ({
id: p.pattern.id,
embedding: p.pattern.embedding,
weight: 1.0,
}));
const coherenceCheck = await coherenceService.checkCoherence(nodes);
// Should provide recommendations
expect(coherenceCheck.recommendations).toBeDefined();
expect(Array.isArray(coherenceCheck.recommendations)).toBe(true);
expect(coherenceCheck.recommendations.length).toBeGreaterThan(0);
// Recommendations should address high energy
if (coherenceCheck.energy > 0.4) {
const hasActionableRec = coherenceCheck.recommendations.some(
r => r.includes('review') || r.includes('resolve') || r.includes('analysis')
);
expect(hasActionableRec).toBe(true);
}
await bank.dispose();
});
});
// ============================================================================
// C. Promotion Coherence Gate
// ============================================================================
describe('Promotion Coherence Gate', () => {
it('should block promotion of incoherent patterns', async () => {
const bank = createQEReasoningBank(memory, eventBus);
await bank.initialize();
// Store a long-term pattern (existing knowledge)
const existingPattern = await bank.storePattern(createTestPattern({
name: 'Established Best Practice',
description: 'Use dependency injection for testability',
context: { tags: ['di', 'best-practice'] },
}));
expect(existingPattern.success).toBe(true);
if (!existingPattern.success) return;
// Manually promote to long-term to simulate existing knowledge
await bank.recordOutcome({
patternId: existingPattern.value.id,
success: true,
});
await bank.recordOutcome({
patternId: existingPattern.value.id,
success: true,
});
await bank.recordOutcome({
patternId: existingPattern.value.id,
success: true,
});
// Create conflicting short-term pattern with good metrics
const conflictingPattern = await bank.storePattern(createTestPattern({
name: 'Avoid Dependency Injection',
description: 'Hardcode dependencies for simplicity',
context: { tags: ['simple', 'no-di'] },
}));
expect(conflictingPattern.success).toBe(true);
if (!conflictingPattern.success) return;
// Give it good metrics
await bank.recordOutcome({
patternId: conflictingPattern.value.id,
success: true,
});
await bank.recordOutcome({
patternId: conflictingPattern.value.id,
success: true,
});
// Check coherence between patterns before promotion
const patterns = [existingPattern.value, conflictingPattern.value];
const nodes = patterns.map(p => ({
id: p.id,
embedding: p.embedding,
weight: p.confidence,
}));
const coherenceCheck = await coherenceService.checkCoherence(nodes);
// In a full implementation, promotion would be blocked if incoherent
// For now, just verify coherence check detected the patterns
expect(coherenceCheck).toBeDefined();
expect(coherenceCheck.energy).toBeGreaterThanOrEqual(0);
// If incoherent, contradictions should be reported
if (!coherenceCheck.isCoherent && coherenceCheck.contradictions.length > 0) {
expect(coherenceCheck.contradictions.length).toBeGreaterThan(0);
// Promotion should be blocked (enforced in actual promotion logic)
}
await bank.dispose();
});
it('should emit promotion_blocked event with reason', async () => {
const bank = createQEReasoningBank(memory, eventBus);
await bank.initialize();
// In a full implementation, this would emit an event when promotion is blocked
// For now, we verify the coherence check mechanism works
const pattern = await bank.storePattern(createTestPattern({
name: 'Contradictory Pattern',
}));
expect(pattern.success).toBe(true);
// The event would be emitted during promotion attempt
// Event structure: { event: 'promotion_blocked', reason: 'coherence_violation', patternId: ... }
await bank.dispose();
});
it('should allow promotion of coherent patterns', async () => {
const bank = createQEReasoningBank(memory, eventBus);
await bank.initialize();
// Create non-conflicting pattern with good metrics
const coherentPattern = await bank.storePattern(createTestPattern({
name: 'Well-Tested Component Pattern',
description: 'Pattern for testing components thoroughly',
context: { tags: ['testing', 'coverage'] },
}));
expect(coherentPattern.success).toBe(true);
if (!coherentPattern.success) return;
// Record successful uses to qualify for promotion (need 4 to get 3 successfulUses)
for (let i = 0; i < 4; i++) {
await bank.recordOutcome({
patternId: coherentPattern.value.id,
success: true,
metrics: { testsPassed: 10, coverageImprovement: 0.15 },
});
}
// Get updated pattern
const updatedPattern = await bank.getPattern(coherentPattern.value.id);
expect(updatedPattern).toBeDefined();
if (!updatedPattern) return;
// Should have promotion-worthy metrics
expect(updatedPattern.successfulUses).toBeGreaterThanOrEqual(3);
expect(updatedPattern.successRate).toBeGreaterThan(0.6);
// Coherence check should pass (no conflicts)
const nodes = [
{
id: updatedPattern.id,
embedding: updatedPattern.embedding,
weight: 1.0,
},
];
const coherenceCheck = await coherenceService.checkCoherence(nodes);
// Single pattern should be coherent
expect(coherenceCheck.isCoherent).toBe(true);
await bank.dispose();
});
it('should skip coherence check when service unavailable', async () => {
const bank = createQEReasoningBank(memory, eventBus);
await bank.initialize();
// Create pattern
const pattern = await bank.storePattern(createTestPattern());
expect(pattern.success).toBe(true);
if (!pattern.success) return;
// Record outcomes to qualify for promotion (need 4 to get 3 successfulUses)
for (let i = 0; i < 4; i++) {
await bank.recordOutcome({
patternId: pattern.value.id,
success: true,
});
}
// When coherence service is unavailable, promotion should proceed
// based only on basic criteria (success rate, usage count)
// This is graceful degradation behavior
const updatedPattern = await bank.getPattern(pattern.value.id);
expect(updatedPattern).toBeDefined();
if (!updatedPattern) return;
// Verify promotion criteria are met
expect(updatedPattern.successfulUses).toBeGreaterThanOrEqual(3);
await bank.dispose();
});
});
// ============================================================================
// D. Causal Verification
// ============================================================================
describe('Causal Verification', () => {
it('should verify causal links using CausalEngine', async () => {
// Test known causal relationship: test coverage -> bug detection
const causalData = {
sampleSize: 50,
causeValues: Array.from({ length: 50 }, (_, i) => i / 50), // Coverage 0-100%
effectValues: Array.from({ length: 50 }, (_, i) => Math.min(1, 0.2 + i / 50 * 0.6)), // Bugs found
confounders: [],
};
const verification = await coherenceService.verifyCausality(
'test_coverage',
'bugs_detected',
causalData
);
expect(verification).toBeDefined();
expect(verification.isCausal).toBeDefined();
expect(verification.effectStrength).toBeGreaterThanOrEqual(0);
// Allow for floating point imprecision
expect(verification.effectStrength).toBeLessThan(1.01);
// Should not be marked as spurious (there's a real correlation)
if (verification.relationshipType) {
expect(verification.relationshipType).not.toBe('spurious');
}
expect(verification.durationMs).toBeGreaterThanOrEqual(0);
});
it('should detect spurious correlations', async () => {
// Test spurious correlation: random data with no causal relationship
const causalData = {
sampleSize: 30,
causeValues: Array.from({ length: 30 }, () => Math.random()), // Random cause
effectValues: Array.from({ length: 30 }, () => Math.random()), // Random effect
confounders: [],
};
const verification = await coherenceService.verifyCausality(
'random_metric_a',
'random_metric_b',
causalData
);
expect(verification).toBeDefined();
// Low correlation should be detected
expect(verification.effectStrength).toBeLessThan(0.5);
// Should be marked as spurious or no relationship
if (verification.relationshipType) {
expect(['spurious', 'none']).toContain(verification.relationshipType);
}
});
it('should return confidence score', async () => {
const causalData = {
sampleSize: 100,
causeValues: Array.from({ length: 100 }, (_, i) => i),
effectValues: Array.from({ length: 100 }, (_, i) => i * 2 + 5), // Strong linear relationship
confounders: [],
};
const verification = await coherenceService.verifyCausality(
'input',
'output',
causalData
);
expect(verification.confidence).toBeDefined();
expect(verification.confidence).toBeGreaterThanOrEqual(0);
expect(verification.confidence).toBeLessThanOrEqual(1);
// Larger sample size should give higher confidence
expect(verification.confidence).toBeGreaterThan(0.3);
});
});
// ============================================================================
// E. Real Integration Tests (Using Actual WASM if Available)
// ============================================================================
describe('Real Integration Tests', () => {
it('should handle real pattern storage and coherence check', async () => {
// This test uses real components without mocks
const realMemory = new InMemoryBackend();
await realMemory.initialize();
const realEventBus = new InMemoryEventBus();
const realBank = createQEReasoningBank(realMemory, realEventBus);
await realBank.initialize();
try {
// Store real patterns
const patterns = [
createTestPattern({
name: 'Test Isolation Pattern',
description: 'Ensure tests are isolated and independent',
}),
createTestPattern({
name: 'Test Cleanup Pattern',
description: 'Clean up resources after each test',
}),
createTestPattern({
name: 'Test Data Builder Pattern',
description: 'Use builders for test data creation',
}),
];
const storedPatterns = [];
for (const pattern of patterns) {
const result = await realBank.storePattern(pattern);
expect(result.success).toBe(true);
if (result.success) {
storedPatterns.push(result.value);
}
}
// Search for patterns
const searchResult = await realBank.searchPatterns('test pattern');
expect(searchResult.success).toBe(true);
if (searchResult.success) {
expect(searchResult.value.length).toBeGreaterThan(0);
// Check coherence of results
const nodes = searchResult.value.slice(0, 3).map(p => ({
id: p.pattern.id,
embedding: p.pattern.embedding,
weight: p.similarity,
}));
const coherenceCheck = await coherenceService.checkCoherence(nodes);
expect(coherenceCheck).toBeDefined();
expect(coherenceCheck.energy).toBeGreaterThanOrEqual(0);
}
// Get stats
const stats = await realBank.getStats();
expect(stats.totalPatterns).toBeGreaterThan(0);
} finally {
await realBank.dispose();
await realMemory.dispose();
await realEventBus.dispose();
}
}, 15000);
it('should handle real coherence service with fallback', async () => {
// Test real coherence service initialization and fallback behavior
const realWasmLoader = createMockWasmLoader();
const realService = new CoherenceService(realWasmLoader as any, {
fallbackEnabled: true,
coherenceThreshold: 0.3,
});
await realService.initialize();
try {
// Create test nodes
const nodes: CoherenceNode[] = [
{
id: 'node1',
embedding: [1.0, 0.0, 0.0, 0.5],
weight: 1.0,
},
{
id: 'node2',
embedding: [0.9, 0.1, 0.0, 0.4],
weight: 1.0,
},
{
id: 'node3',
embedding: [-1.0, 0.0, 0.0, -0.5], // Opposite direction
weight: 1.0,
},
];
const result = await realService.checkCoherence(nodes);
expect(result).toBeDefined();
expect(result.energy).toBeGreaterThanOrEqual(0);
expect(result.isCoherent).toBeDefined();
expect(result.lane).toMatch(/^(reflex|retrieval|heavy|human)$/);
expect(result.usedFallback).toBe(true); // Should use fallback
// Get stats
const stats = realService.getStats();
expect(stats.totalChecks).toBeGreaterThan(0);
expect(stats.fallbackCount).toBeGreaterThan(0);
expect(stats.wasmAvailable).toBe(false);
} finally {
await realService.dispose();
}
});
});
});