diff --git a/.memory/audit/decisions-2025-10-09.jsonl b/.memory/audit/decisions-2025-10-09.jsonl new file mode 100644 index 00000000..9a7474ba --- /dev/null +++ b/.memory/audit/decisions-2025-10-09.jsonl @@ -0,0 +1,3 @@ +{"timestamp":"2025-10-09T23:32:13.911Z","sessionId":"production-deployment-test","action":"boundary_enforcement","rulesChecked":["inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"boundary":"none","domain":"TECHNICAL","requirementType":"NONE","actionType":"deployment_test","enforcement_decision":"ALLOWED"}} +{"timestamp":"2025-10-09T23:39:11.351Z","sessionId":"session1-integration-test","action":"instruction_classification","rulesChecked":["inst_001","inst_002","inst_003","inst_004","inst_005","inst_006","inst_007","inst_008","inst_009","inst_010","inst_011","inst_012","inst_013","inst_014","inst_015","inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"instruction_text":"Always check port 27027 for MongoDB connections","quadrant":"STRATEGIC","persistence":"HIGH","persistence_score":0.9,"explicitness":0.85,"verification":"MANDATORY","temporal_scope":"PERMANENT","source":"user","recency_weight":0.9999986111120757,"parameters":{"port":"27027"}}} +{"timestamp":"2025-10-09T23:39:11.354Z","sessionId":"session1-integration-test","action":"cross_reference_validation","rulesChecked":["instruction"],"violations":["Always check port 27027 for MongoDB connections"],"allowed":false,"metadata":{"action_description":"Connect to MongoDB on port 27017","validation_status":"REJECTED","conflicts_found":1,"critical_conflicts":1,"relevant_instructions":1,"validation_action":"REQUEST_CLARIFICATION","conflict_details":[{"parameter":"port","severity":"CRITICAL","action_value":"27017","instruction_value":"27027"}]}} diff --git a/scripts/test-session1-integration.js b/scripts/test-session1-integration.js new file mode 100755 index 00000000..8eb06afa --- /dev/null +++ b/scripts/test-session1-integration.js @@ -0,0 +1,199 @@ +#!/usr/bin/env node + +/** + * Session 1 Integration Test + * Validates InstructionPersistenceClassifier and CrossReferenceValidator + * integration with MemoryProxy + */ + +const InstructionPersistenceClassifier = require('../src/services/InstructionPersistenceClassifier.service'); +const CrossReferenceValidator = require('../src/services/CrossReferenceValidator.service'); +const { getMemoryProxy } = require('../src/services/MemoryProxy.service'); +const fs = require('fs').promises; +const path = require('path'); + +async function testSession1Integration() { + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log(' Session 1 Integration Test'); + console.log(' InstructionPersistenceClassifier + CrossReferenceValidator'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + const results = { + memoryProxy: { initialized: false }, + classifier: { initialized: false, referenceRulesLoaded: 0 }, + validator: { initialized: false, governanceRulesLoaded: 0 }, + classificationTest: { passed: false }, + validationTest: { passed: false }, + auditTrail: { exists: false, entries: 0 } + }; + + try { + // Step 1: Initialize MemoryProxy (shared singleton) + console.log('[Step 1] Initializing MemoryProxy...'); + const memoryProxy = getMemoryProxy(); + await memoryProxy.initialize(); + results.memoryProxy.initialized = true; + console.log(' ✓ MemoryProxy initialized\n'); + + // Step 2: Initialize InstructionPersistenceClassifier + console.log('[Step 2] Initializing InstructionPersistenceClassifier...'); + const classifierResult = await InstructionPersistenceClassifier.initialize(); + + if (classifierResult.success) { + results.classifier.initialized = true; + results.classifier.referenceRulesLoaded = classifierResult.referenceRulesLoaded; + console.log(` ✓ InstructionPersistenceClassifier initialized`); + console.log(` Reference rules loaded: ${classifierResult.referenceRulesLoaded}\n`); + } else { + throw new Error(`Classifier initialization failed: ${classifierResult.error}`); + } + + // Step 3: Initialize CrossReferenceValidator + console.log('[Step 3] Initializing CrossReferenceValidator...'); + const validatorResult = await CrossReferenceValidator.initialize(); + + if (validatorResult.success) { + results.validator.initialized = true; + results.validator.governanceRulesLoaded = validatorResult.governanceRulesLoaded; + console.log(` ✓ CrossReferenceValidator initialized`); + console.log(` Governance rules loaded: ${validatorResult.governanceRulesLoaded}\n`); + } else { + throw new Error(`Validator initialization failed: ${validatorResult.error}`); + } + + // Step 4: Test classification with audit + console.log('[Step 4] Testing classification with audit trail...'); + + const testInstruction = { + text: 'Always check port 27027 for MongoDB connections', + context: { sessionId: 'session1-integration-test' }, + timestamp: new Date(), + source: 'user' + }; + + const classification = InstructionPersistenceClassifier.classify(testInstruction); + + console.log(` ✓ Classification result:`); + console.log(` Quadrant: ${classification.quadrant}`); + console.log(` Persistence: ${classification.persistence}`); + console.log(` Verification: ${classification.verification}`); + console.log(` Explicitness: ${classification.explicitness.toFixed(2)}\n`); + + if (classification.quadrant && classification.persistence) { + results.classificationTest.passed = true; + } + + // Step 5: Test validation with audit + console.log('[Step 5] Testing validation with audit trail...'); + + const testAction = { + description: 'Connect to MongoDB on port 27017', + parameters: { port: '27017' } + }; + + const testContext = { + sessionId: 'session1-integration-test', + recent_instructions: [classification] + }; + + const validation = CrossReferenceValidator.validate(testAction, testContext); + + console.log(` ✓ Validation result:`); + console.log(` Status: ${validation.status}`); + console.log(` Conflicts: ${validation.conflicts?.length || 0}`); + console.log(` Action: ${validation.action}\n`); + + if (validation.status) { + results.validationTest.passed = true; + } + + // Step 6: Verify audit trail (wait for async writes) + console.log('[Step 6] Verifying audit trail...'); + + // Wait for async audit writes + await new Promise(resolve => setTimeout(resolve, 100)); + + const today = new Date().toISOString().split('T')[0]; + const auditPath = path.join(__dirname, '../.memory/audit', `decisions-${today}.jsonl`); + + try { + const auditData = await fs.readFile(auditPath, 'utf8'); + const auditLines = auditData.trim().split('\n'); + + // Filter for session1 entries + const session1Entries = auditLines.filter(line => { + try { + const entry = JSON.parse(line); + return entry.sessionId === 'session1-integration-test'; + } catch { + return false; + } + }); + + results.auditTrail.exists = true; + results.auditTrail.entries = session1Entries.length; + + console.log(` ✓ Audit trail exists: ${auditPath}`); + console.log(` Session 1 entries: ${session1Entries.length}`); + + if (session1Entries.length > 0) { + console.log('\n Sample entries:'); + session1Entries.slice(0, 2).forEach((line, idx) => { + const entry = JSON.parse(line); + console.log(` ${idx + 1}. Action: ${entry.action} | Allowed: ${entry.allowed}`); + }); + } + } catch (error) { + console.log(` ⚠ Audit trail check: ${error.message}`); + } + + console.log(); + + } catch (error) { + console.error(`\n✗ Integration test failed: ${error.message}\n`); + if (error.stack) { + console.error('Stack trace:', error.stack); + } + process.exit(1); + } + + // Results summary + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log(' INTEGRATION TEST RESULTS'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + console.log('✅ SESSION 1 INTEGRATION SUCCESSFUL\n'); + + console.log('Services Initialized:'); + console.log(` • MemoryProxy: ${results.memoryProxy.initialized ? '✅' : '❌'}`); + console.log(` • InstructionPersistenceClassifier: ${results.classifier.initialized ? '✅' : '❌'} (${results.classifier.referenceRulesLoaded} reference rules)`); + console.log(` • CrossReferenceValidator: ${results.validator.initialized ? '✅' : '❌'} (${results.validator.governanceRulesLoaded} governance rules)`); + + console.log('\nFunctionality Tests:'); + console.log(` • Classification with audit: ${results.classificationTest.passed ? '✅' : '❌'}`); + console.log(` • Validation with audit: ${results.validationTest.passed ? '✅' : '❌'}`); + + console.log('\nAudit Trail:'); + console.log(` • Created: ${results.auditTrail.exists ? '✅' : '❌'}`); + console.log(` • Session 1 entries: ${results.auditTrail.entries}`); + + console.log('\n📊 Integration Status: 🟢 OPERATIONAL'); + console.log('\nIntegration Progress:'); + console.log(' • Session 1: 4/6 services integrated (67%)'); + console.log(' • BoundaryEnforcer: ✅ (Week 3)'); + console.log(' • BlogCuration: ✅ (Week 3)'); + console.log(' • InstructionPersistenceClassifier: ✅ (Session 1)'); + console.log(' • CrossReferenceValidator: ✅ (Session 1)'); + console.log(' • MetacognitiveVerifier: ⏳ (Session 2)'); + console.log(' • ContextPressureMonitor: ⏳ (Session 2)'); + + console.log('\nNext Steps:'); + console.log(' 1. ✅ Core services integrated (4/6)'); + console.log(' 2. 🔄 Session 2: Integrate MetacognitiveVerifier + ContextPressureMonitor'); + console.log(' 3. 🔄 Target: 100% service integration'); + + console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); +} + +// Run test +testSession1Integration(); diff --git a/src/services/CrossReferenceValidator.service.js b/src/services/CrossReferenceValidator.service.js index 12003c5e..86dcc408 100644 --- a/src/services/CrossReferenceValidator.service.js +++ b/src/services/CrossReferenceValidator.service.js @@ -28,6 +28,7 @@ */ const classifier = require('./InstructionPersistenceClassifier.service'); +const { getMemoryProxy } = require('./MemoryProxy.service'); const logger = require('../utils/logger.util'); /** @@ -58,6 +59,11 @@ class CrossReferenceValidator { this.instructionCache = new Map(); // Cache classified instructions this.instructionHistory = []; // Recent instruction history + // Initialize MemoryProxy for governance rules and audit logging + this.memoryProxy = getMemoryProxy(); + this.governanceRules = []; // Loaded from memory + this.memoryProxyInitialized = false; + // Statistics tracking this.stats = { total_validations: 0, @@ -76,6 +82,41 @@ class CrossReferenceValidator { logger.info('CrossReferenceValidator initialized'); } + /** + * Initialize MemoryProxy and load governance rules + * @returns {Promise} Initialization result + */ + async initialize() { + try { + await this.memoryProxy.initialize(); + + // Load all governance rules for validation reference + this.governanceRules = await this.memoryProxy.loadGovernanceRules(); + + this.memoryProxyInitialized = true; + + logger.info('[CrossReferenceValidator] MemoryProxy initialized', { + governanceRulesLoaded: this.governanceRules.length + }); + + return { + success: true, + governanceRulesLoaded: this.governanceRules.length + }; + + } catch (error) { + logger.error('[CrossReferenceValidator] Failed to initialize MemoryProxy', { + error: error.message + }); + // Continue with existing validation logic even if memory fails + return { + success: false, + error: error.message, + governanceRulesLoaded: 0 + }; + } + } + /** * Validate a proposed action against conversation context * @param {Object} action - The proposed action @@ -108,7 +149,12 @@ class CrossReferenceValidator { } // Make validation decision based on conflicts - return this._makeValidationDecision(conflicts, action); + const decision = this._makeValidationDecision(conflicts, action); + + // Audit validation decision + this._auditValidation(decision, action, relevantInstructions, context); + + return decision; } catch (error) { logger.error('Validation error:', error); @@ -501,6 +547,50 @@ class CrossReferenceValidator { this.instructionCache.clear(); } + /** + * Audit validation decision to memory (async, non-blocking) + * @private + */ + _auditValidation(decision, action, relevantInstructions, context = {}) { + // Only audit if MemoryProxy is initialized + if (!this.memoryProxyInitialized) { + return; + } + + // Extract violation information + const violations = decision.conflicts + ?.filter(c => c.severity === CONFLICT_SEVERITY.CRITICAL) + .map(c => c.instruction?.text || c.parameter) || []; + + // Audit asynchronously (don't block validation) + this.memoryProxy.auditDecision({ + sessionId: context.sessionId || 'validator-service', + action: 'cross_reference_validation', + rulesChecked: relevantInstructions.map(i => i.id || 'instruction'), + violations, + allowed: decision.status === VALIDATION_STATUS.APPROVED, + metadata: { + action_description: action.description?.substring(0, 100), + validation_status: decision.status, + conflicts_found: decision.conflicts?.length || 0, + critical_conflicts: violations.length, + relevant_instructions: relevantInstructions.length, + validation_action: decision.action, + conflict_details: decision.conflicts?.slice(0, 3).map(c => ({ + parameter: c.parameter, + severity: c.severity, + action_value: c.actionValue, + instruction_value: c.instructionValue + })) || [] + } + }).catch(error => { + logger.error('[CrossReferenceValidator] Failed to audit validation', { + error: error.message, + action: action.description?.substring(0, 50) + }); + }); + } + /** * Get validation statistics * @returns {Object} Statistics object diff --git a/src/services/InstructionPersistenceClassifier.service.js b/src/services/InstructionPersistenceClassifier.service.js index a6288488..0d379c5e 100644 --- a/src/services/InstructionPersistenceClassifier.service.js +++ b/src/services/InstructionPersistenceClassifier.service.js @@ -26,6 +26,7 @@ */ const logger = require('../utils/logger.util'); +const { getMemoryProxy } = require('./MemoryProxy.service'); /** * Quadrant definitions from Tractatus framework @@ -122,6 +123,11 @@ class InstructionPersistenceClassifier { this.quadrants = QUADRANTS; this.persistenceLevels = PERSISTENCE_LEVELS; + // Initialize MemoryProxy for reference rules and audit logging + this.memoryProxy = getMemoryProxy(); + this.referenceRules = []; // Loaded from memory for pattern matching + this.memoryProxyInitialized = false; + // Compile keyword patterns for efficient matching this.keywordPatterns = this._compileKeywordPatterns(); @@ -152,6 +158,41 @@ class InstructionPersistenceClassifier { logger.info('InstructionPersistenceClassifier initialized'); } + /** + * Initialize MemoryProxy and load reference rules + * @returns {Promise} Initialization result + */ + async initialize() { + try { + await this.memoryProxy.initialize(); + + // Load all rules as reference for pattern matching + this.referenceRules = await this.memoryProxy.loadGovernanceRules(); + + this.memoryProxyInitialized = true; + + logger.info('[InstructionPersistenceClassifier] MemoryProxy initialized', { + referenceRulesLoaded: this.referenceRules.length + }); + + return { + success: true, + referenceRulesLoaded: this.referenceRules.length + }; + + } catch (error) { + logger.error('[InstructionPersistenceClassifier] Failed to initialize MemoryProxy', { + error: error.message + }); + // Continue with existing classification logic even if memory fails + return { + success: false, + error: error.message, + referenceRulesLoaded: 0 + }; + } + } + /** * Classify an instruction or action * @param {Object} params @@ -237,6 +278,9 @@ class InstructionPersistenceClassifier { verification }); + // Audit classification decision + this._auditClassification(classification, context); + return classification; } catch (error) { @@ -666,6 +710,43 @@ class InstructionPersistenceClassifier { }; } + /** + * Audit classification decision to memory (async, non-blocking) + * @private + */ + _auditClassification(classification, context = {}) { + // Only audit if MemoryProxy is initialized + if (!this.memoryProxyInitialized) { + return; + } + + // Audit asynchronously (don't block classification) + this.memoryProxy.auditDecision({ + sessionId: context.sessionId || 'classifier-service', + action: 'instruction_classification', + rulesChecked: this.referenceRules.map(r => r.id), + violations: [], // Classification doesn't detect violations + allowed: true, // Classification is always allowed + metadata: { + instruction_text: classification.text.substring(0, 100), + quadrant: classification.quadrant, + persistence: classification.persistence, + persistence_score: classification.persistenceScore, + explicitness: classification.explicitness, + verification: classification.verification, + temporal_scope: classification.metadata.temporalScope, + source: classification.source, + recency_weight: classification.recencyWeight, + parameters: classification.parameters + } + }).catch(error => { + logger.error('[InstructionPersistenceClassifier] Failed to audit classification', { + error: error.message, + text: classification.text.substring(0, 50) + }); + }); + } + /** * Get classification statistics * @returns {Object} Statistics object