feat(framework): implement Phase 3 bidirectional communication architecture
Phase 3.5: Cross-validation between prompt analysis and action analysis - Added prompt-analyzer-hook.js to store prompt expectations in session state - Modified framework-audit-hook.js to retrieve and compare prompt vs action - Implemented cross-validation logic tracking agreements, disagreements, missed flags - Added validation feedback to systemMessage for real-time guidance Services enhanced with guidance generation: - BoundaryEnforcer: _buildGuidance() provides systemMessage for enforcement decisions - CrossReferenceValidator: Generates guidance for cross-reference conflicts - MetacognitiveVerifier: Provides guidance on metacognitive verification - PluralisticDeliberationOrchestrator: Offers guidance on values conflicts Framework now communicates bidirectionally: - TO Claude: systemMessage injection with proactive guidance - FROM Claude: Audit logs with framework_backed_decision metadata Integration testing: 92% success (23/25 tests passed) Recent performance: 100% guidance generation for new decisions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
97ed03d180
commit
8ee2f73928
6 changed files with 1217 additions and 32 deletions
|
|
@ -129,15 +129,18 @@ async function main() {
|
|||
// Route to framework service based on tool
|
||||
let decision = 'allow';
|
||||
let reason = 'Framework check passed';
|
||||
let systemMessage = null; // PHASE 3: Collect systemMessage
|
||||
|
||||
if (tool_name === 'Edit' || tool_name === 'Write') {
|
||||
const result = await handleFileModification(tool_input, sessionId);
|
||||
decision = result.decision;
|
||||
reason = result.reason;
|
||||
systemMessage = result.systemMessage; // PHASE 3: Capture systemMessage
|
||||
} else if (tool_name === 'Bash') {
|
||||
const result = await handleBashCommand(tool_input, sessionId);
|
||||
decision = result.decision;
|
||||
reason = result.reason;
|
||||
systemMessage = result.systemMessage; // PHASE 3: Capture systemMessage
|
||||
}
|
||||
|
||||
// Wait for async audit logging to complete before disconnecting
|
||||
|
|
@ -145,7 +148,7 @@ async function main() {
|
|||
|
||||
await mongoose.disconnect();
|
||||
|
||||
outputResponse(decision, reason);
|
||||
outputResponse(decision, reason, systemMessage); // PHASE 3: Pass systemMessage
|
||||
process.exit(decision === 'deny' ? 2 : 0);
|
||||
|
||||
} catch (err) {
|
||||
|
|
@ -168,6 +171,81 @@ async function handleFileModification(toolInput, sessionId) {
|
|||
const filePath = toolInput.file_path || toolInput.path || 'unknown';
|
||||
const content = toolInput.new_string || toolInput.content || '';
|
||||
|
||||
// PHASE 3.5: Cross-validate prompt analysis vs action analysis
|
||||
let promptAnalysis = null;
|
||||
let crossValidation = {
|
||||
hasPromptAnalysis: false,
|
||||
agreements: [],
|
||||
disagreements: [],
|
||||
missedFlags: [],
|
||||
validationScore: 0
|
||||
};
|
||||
|
||||
try {
|
||||
const sessionStatePath = path.join(__dirname, '..', 'session-state.json');
|
||||
if (fs.existsSync(sessionStatePath)) {
|
||||
const sessionState = JSON.parse(fs.readFileSync(sessionStatePath, 'utf-8'));
|
||||
promptAnalysis = sessionState.promptAnalysis?.latest || null;
|
||||
|
||||
if (promptAnalysis) {
|
||||
crossValidation.hasPromptAnalysis = true;
|
||||
|
||||
// Detect action characteristics
|
||||
const lowerPath = filePath.toLowerCase();
|
||||
const lowerContent = content.toLowerCase();
|
||||
|
||||
const actionFlags = {
|
||||
isSchemaChange: lowerPath.includes('model') || lowerPath.includes('schema') ||
|
||||
lowerContent.includes('schema') || lowerContent.includes('mongoose'),
|
||||
isSecurityChange: lowerPath.includes('auth') || lowerPath.includes('jwt') ||
|
||||
lowerPath.includes('security') || lowerContent.includes('password'),
|
||||
isServiceFile: lowerPath.includes('service.js'),
|
||||
isHookFile: lowerPath.includes('hook'),
|
||||
isConfigFile: lowerPath.includes('config')
|
||||
};
|
||||
|
||||
// Compare prompt expectations vs actual action
|
||||
// 1. Schema change validation
|
||||
if (promptAnalysis.flags.schemaChange === true && actionFlags.isSchemaChange === true) {
|
||||
crossValidation.agreements.push('schema_change_detected');
|
||||
} else if (promptAnalysis.flags.schemaChange === true && actionFlags.isSchemaChange === false) {
|
||||
crossValidation.disagreements.push('prompt_expected_schema_but_action_is_not');
|
||||
} else if (promptAnalysis.flags.schemaChange === false && actionFlags.isSchemaChange === true) {
|
||||
crossValidation.missedFlags.push('action_is_schema_but_prompt_didnt_detect');
|
||||
}
|
||||
|
||||
// 2. Security change validation
|
||||
if (promptAnalysis.flags.securityChange === true && actionFlags.isSecurityChange === true) {
|
||||
crossValidation.agreements.push('security_change_detected');
|
||||
} else if (promptAnalysis.flags.securityChange === true && actionFlags.isSecurityChange === false) {
|
||||
crossValidation.disagreements.push('prompt_expected_security_but_action_is_not');
|
||||
} else if (promptAnalysis.flags.securityChange === false && actionFlags.isSecurityChange === true) {
|
||||
crossValidation.missedFlags.push('action_is_security_but_prompt_didnt_detect');
|
||||
}
|
||||
|
||||
// Calculate validation score (0-100)
|
||||
const totalChecks = crossValidation.agreements.length +
|
||||
crossValidation.disagreements.length +
|
||||
crossValidation.missedFlags.length;
|
||||
|
||||
if (totalChecks > 0) {
|
||||
crossValidation.validationScore = Math.round(
|
||||
(crossValidation.agreements.length / totalChecks) * 100
|
||||
);
|
||||
} else {
|
||||
// No flags to validate - perfect alignment
|
||||
crossValidation.validationScore = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Non-fatal: Continue without cross-validation
|
||||
console.error('[Framework Audit] Cross-validation error:', err.message);
|
||||
}
|
||||
|
||||
// PHASE 3: Collect guidance from all framework services
|
||||
const guidanceMessages = [];
|
||||
|
||||
// 1. Boundary enforcement
|
||||
const action = {
|
||||
type: 'file_modification',
|
||||
|
|
@ -184,6 +262,11 @@ async function handleFileModification(toolInput, sessionId) {
|
|||
|
||||
const boundaryResult = BoundaryEnforcer.enforce(action, context);
|
||||
|
||||
// PHASE 3: Collect guidance from BoundaryEnforcer
|
||||
if (boundaryResult.guidance && boundaryResult.guidance.systemMessage) {
|
||||
guidanceMessages.push(boundaryResult.guidance.systemMessage);
|
||||
}
|
||||
|
||||
// If boundary enforcer blocks, deny the action
|
||||
if (!boundaryResult.allowed) {
|
||||
return {
|
||||
|
|
@ -192,6 +275,41 @@ async function handleFileModification(toolInput, sessionId) {
|
|||
};
|
||||
}
|
||||
|
||||
// 1a. PHASE 2: Semantic detection - schema changes
|
||||
const schemaDetection = BoundaryEnforcer.detectSchemaChange(filePath, content);
|
||||
|
||||
if (schemaDetection.isSchemaChange) {
|
||||
// Schema change detected - invoke CrossReferenceValidator
|
||||
const schemaValidation = await CrossReferenceValidator.validateSchemaChange({
|
||||
type: 'schema_modification',
|
||||
description: `${schemaDetection.isSensitiveCollection ? 'SENSITIVE ' : ''}Schema change in ${path.basename(filePath)}`,
|
||||
file: filePath,
|
||||
severity: schemaDetection.severity,
|
||||
patterns: schemaDetection.detectedPatterns
|
||||
}, {
|
||||
...context,
|
||||
schema_change: true,
|
||||
sensitive_collection: schemaDetection.isSensitiveCollection
|
||||
});
|
||||
|
||||
// PHASE 3: Collect guidance from schema validation
|
||||
if (schemaValidation.guidance && schemaValidation.guidance.systemMessage) {
|
||||
guidanceMessages.push(schemaValidation.guidance.systemMessage);
|
||||
}
|
||||
|
||||
// If critical conflicts found, block the action
|
||||
if (!schemaValidation.allowed && schemaValidation.criticalConflicts > 0) {
|
||||
return {
|
||||
decision: 'deny',
|
||||
reason: `BLOCKED: ${schemaValidation.recommendation}`,
|
||||
systemMessage: guidanceMessages.join('\n') // Include collected guidance
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 1b. PHASE 2: Semantic detection - security gradient
|
||||
const securityGradient = BoundaryEnforcer.detectSecurityGradient(filePath, content);
|
||||
|
||||
// 2. HARD BLOCK: instruction-history.json modifications (inst_027)
|
||||
if (filePath.includes('instruction-history.json')) {
|
||||
return {
|
||||
|
|
@ -219,24 +337,33 @@ async function handleFileModification(toolInput, sessionId) {
|
|||
CrossReferenceValidator.validate(validateAction, { ...context, governance: true });
|
||||
}
|
||||
|
||||
// 4. Metacognitive verification for security-critical files
|
||||
const securityFiles = ['auth', 'security', 'credential', 'jwt', 'password', 'secret'];
|
||||
const isSecurityFile = securityFiles.some(keyword => filePath.toLowerCase().includes(keyword));
|
||||
|
||||
if (isSecurityFile) {
|
||||
// 4. PHASE 2: Graduated security verification based on content analysis
|
||||
if (securityGradient !== 'ROUTINE') {
|
||||
const verifyAction = {
|
||||
type: 'modify_security_file',
|
||||
description: `Modifying security-critical file: ${filePath}`,
|
||||
file: filePath
|
||||
description: `[${securityGradient}] Modifying ${path.basename(filePath)}`,
|
||||
file: filePath,
|
||||
security_gradient: securityGradient
|
||||
};
|
||||
|
||||
const reasoning = `Modifying security-critical file: ${filePath}. Automated approval for documentation/comments.`;
|
||||
// Determine automated_approval based on security gradient
|
||||
const canAutoApprove = securityGradient === 'DOCUMENTATION';
|
||||
const reasoning = canAutoApprove
|
||||
? `${securityGradient} level change to ${filePath}. Documentation/comment update - automated approval.`
|
||||
: `${securityGradient} level security modification detected in ${filePath}. Requires elevated scrutiny.`;
|
||||
|
||||
MetacognitiveVerifier.verify(verifyAction, reasoning, {
|
||||
const verificationResult = MetacognitiveVerifier.verify(verifyAction, reasoning, {
|
||||
...context,
|
||||
security_impact: true,
|
||||
automated_approval: true
|
||||
security_impact: securityGradient === 'CRITICAL' || securityGradient === 'HIGH',
|
||||
security_gradient: securityGradient,
|
||||
automated_approval: canAutoApprove,
|
||||
requires_human_review: securityGradient === 'CRITICAL'
|
||||
});
|
||||
|
||||
// PHASE 3: Collect guidance from MetacognitiveVerifier
|
||||
if (verificationResult && verificationResult.guidance && verificationResult.guidance.systemMessage) {
|
||||
guidanceMessages.push(verificationResult.guidance.systemMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Context pressure monitoring (on every tool use)
|
||||
|
|
@ -274,7 +401,7 @@ async function handleFileModification(toolInput, sessionId) {
|
|||
if (hasValueConflict) {
|
||||
const PluralisticDeliberationOrchestrator = require('../../src/services/PluralisticDeliberationOrchestrator.service');
|
||||
|
||||
PluralisticDeliberationOrchestrator.analyzeConflict({
|
||||
const deliberationResult = PluralisticDeliberationOrchestrator.analyzeConflict({
|
||||
type: 'file_modification',
|
||||
description: `Modifying file with potential value conflicts: ${path.basename(filePath)}`,
|
||||
file: filePath
|
||||
|
|
@ -282,12 +409,49 @@ async function handleFileModification(toolInput, sessionId) {
|
|||
...context,
|
||||
value_domains: valueConflictFiles.filter(k => filePath.toLowerCase().includes(k))
|
||||
});
|
||||
|
||||
// PHASE 3: Collect guidance from PluralisticDeliberationOrchestrator
|
||||
if (deliberationResult && deliberationResult.guidance && deliberationResult.guidance.systemMessage) {
|
||||
guidanceMessages.push(deliberationResult.guidance.systemMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// PHASE 3.5: Add cross-validation feedback to systemMessage
|
||||
if (crossValidation.hasPromptAnalysis) {
|
||||
if (crossValidation.missedFlags.length > 0) {
|
||||
guidanceMessages.push(
|
||||
`\n⚠️ CROSS-VALIDATION: Prompt analysis missed ${crossValidation.missedFlags.length} flag(s):\n` +
|
||||
crossValidation.missedFlags.map(f => ` • ${f}`).join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
if (crossValidation.disagreements.length > 0) {
|
||||
guidanceMessages.push(
|
||||
`\n⚠️ CROSS-VALIDATION: ${crossValidation.disagreements.length} disagreement(s) between prompt and action:\n` +
|
||||
crossValidation.disagreements.map(d => ` • ${d}`).join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
if (crossValidation.validationScore < 80 && crossValidation.validationScore > 0) {
|
||||
guidanceMessages.push(
|
||||
`\n📊 VALIDATION SCORE: ${crossValidation.validationScore}% (prompt-action alignment)`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// PHASE 3: Aggregate guidance and return with systemMessage
|
||||
const result = {
|
||||
decision: 'allow',
|
||||
reason: `Framework audit complete: ${path.basename(filePath)}`
|
||||
reason: `Framework audit complete: ${path.basename(filePath)}`,
|
||||
crossValidation // PHASE 3.5: Include validation data
|
||||
};
|
||||
|
||||
// Add systemMessage if we have any guidance
|
||||
if (guidanceMessages.length > 0) {
|
||||
result.systemMessage = guidanceMessages.join('\n');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -298,6 +462,9 @@ async function handleBashCommand(toolInput, sessionId) {
|
|||
|
||||
const command = toolInput.command || '';
|
||||
|
||||
// PHASE 3: Collect guidance
|
||||
const guidanceMessages = [];
|
||||
|
||||
// Check for cross-project commands
|
||||
const crossProjectPatterns = [
|
||||
'/family-history/',
|
||||
|
|
@ -322,10 +489,16 @@ async function handleBashCommand(toolInput, sessionId) {
|
|||
|
||||
const result = BoundaryEnforcer.enforce(action, context);
|
||||
|
||||
// PHASE 3: Collect guidance from BoundaryEnforcer
|
||||
if (result.guidance && result.guidance.systemMessage) {
|
||||
guidanceMessages.push(result.guidance.systemMessage);
|
||||
}
|
||||
|
||||
if (!result.allowed) {
|
||||
return {
|
||||
decision: 'deny',
|
||||
reason: result.message || 'Bash command blocked by BoundaryEnforcer'
|
||||
reason: result.message || 'Bash command blocked by BoundaryEnforcer',
|
||||
systemMessage: guidanceMessages.join('\n') // Include collected guidance
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -338,10 +511,17 @@ async function handleBashCommand(toolInput, sessionId) {
|
|||
command: command.substring(0, 100)
|
||||
});
|
||||
|
||||
return {
|
||||
// PHASE 3: Return with aggregated guidance
|
||||
const response = {
|
||||
decision: 'allow',
|
||||
reason: 'Bash command allowed'
|
||||
};
|
||||
|
||||
if (guidanceMessages.length > 0) {
|
||||
response.systemMessage = guidanceMessages.join('\n');
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// Run hook
|
||||
|
|
|
|||
323
.claude/hooks/prompt-analyzer-hook.js
Executable file
323
.claude/hooks/prompt-analyzer-hook.js
Executable file
|
|
@ -0,0 +1,323 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Prompt Analyzer Hook (UserPromptSubmit)
|
||||
*
|
||||
* Analyzes user prompts BEFORE Claude processes them to provide
|
||||
* framework-backed guidance for governance decisions.
|
||||
*
|
||||
* This transforms the framework from passive observer to active participant
|
||||
* by injecting analysis into Claude's reasoning context.
|
||||
*
|
||||
* Hook Input (JSON via stdin):
|
||||
* {
|
||||
* "session_id": "abc123",
|
||||
* "hook_event_name": "UserPromptSubmit",
|
||||
* "user_message": "Remove accessibility features for performance"
|
||||
* }
|
||||
*
|
||||
* Hook Output (JSON to stdout):
|
||||
* {
|
||||
* "hookSpecificOutput": {
|
||||
* "hookEventName": "UserPromptSubmit",
|
||||
* "frameworkAnalysis": {...}
|
||||
* },
|
||||
* "systemMessage": "🔍 FRAMEWORK: Value conflict detected...",
|
||||
* "continue": true,
|
||||
* "suppressOutput": false
|
||||
* }
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Read JSON input from stdin
|
||||
*/
|
||||
function readStdin() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let data = '';
|
||||
process.stdin.on('data', chunk => { data += chunk; });
|
||||
process.stdin.on('end', () => {
|
||||
try {
|
||||
resolve(JSON.parse(data));
|
||||
} catch (err) {
|
||||
reject(new Error('Invalid JSON input'));
|
||||
}
|
||||
});
|
||||
process.stdin.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Output hook response
|
||||
*/
|
||||
function outputResponse(frameworkAnalysis, systemMessage = null) {
|
||||
const response = {
|
||||
hookSpecificOutput: {
|
||||
hookEventName: 'UserPromptSubmit',
|
||||
frameworkAnalysis
|
||||
},
|
||||
continue: true,
|
||||
suppressOutput: false
|
||||
};
|
||||
|
||||
if (systemMessage) {
|
||||
response.systemMessage = systemMessage;
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect value keywords in text
|
||||
*/
|
||||
function detectValueKeywords(text, valueKeywords) {
|
||||
const detected = [];
|
||||
|
||||
for (const [value, keywords] of Object.entries(valueKeywords)) {
|
||||
if (keywords.some(k => text.toLowerCase().includes(k))) {
|
||||
detected.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
return detected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main hook logic
|
||||
*/
|
||||
async function main() {
|
||||
let input;
|
||||
|
||||
try {
|
||||
input = await readStdin();
|
||||
} catch (err) {
|
||||
// Invalid input, allow execution without analysis
|
||||
outputResponse({ error: 'Invalid hook input' });
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const { session_id, user_message } = input;
|
||||
|
||||
if (!user_message || user_message.trim().length === 0) {
|
||||
// Empty message, no analysis needed
|
||||
outputResponse({ note: 'Empty message' });
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Connect to MongoDB and invoke framework
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
try {
|
||||
await mongoose.connect('mongodb://localhost:27017/tractatus_dev', {
|
||||
serverSelectionTimeoutMS: 2000
|
||||
});
|
||||
} catch (err) {
|
||||
// MongoDB not available, allow execution but no framework analysis
|
||||
outputResponse({
|
||||
error: 'Framework unavailable (MongoDB not connected)',
|
||||
message: user_message
|
||||
});
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Import framework services
|
||||
const InstructionPersistenceClassifier = require('../../src/services/InstructionPersistenceClassifier.service');
|
||||
const PluralisticDeliberationOrchestrator = require('../../src/services/PluralisticDeliberationOrchestrator.service');
|
||||
|
||||
// Initialize services
|
||||
try {
|
||||
await InstructionPersistenceClassifier.initialize();
|
||||
await PluralisticDeliberationOrchestrator.initialize();
|
||||
} catch (err) {
|
||||
// Initialization failed, continue without framework
|
||||
await mongoose.disconnect();
|
||||
outputResponse({
|
||||
error: 'Framework initialization failed',
|
||||
message: err.message
|
||||
});
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const sessionId = session_id || 'claude-code-session';
|
||||
|
||||
try {
|
||||
// 1. Classify instruction
|
||||
const classification = InstructionPersistenceClassifier.classify({
|
||||
text: user_message,
|
||||
context: { sessionId, source: 'user' },
|
||||
timestamp: new Date(),
|
||||
source: 'user'
|
||||
});
|
||||
|
||||
// 2. Detect value keywords
|
||||
const valueKeywords = {
|
||||
accessibility: ['accessibility', 'a11y', 'screen reader', 'wcag', 'aria', 'alt text'],
|
||||
security: ['auth', 'security', 'rate limit', 'credential', 'password', 'encryption', 'https'],
|
||||
performance: ['optimize', 'performance', 'speed', 'fast', 'cache', 'load time'],
|
||||
privacy: ['privacy', 'data', 'tracking', 'user data', 'personal information', 'gdpr']
|
||||
};
|
||||
|
||||
const detectedValues = detectValueKeywords(user_message, valueKeywords);
|
||||
|
||||
// 3. Check for value trade-offs (red flag patterns)
|
||||
const tradeoffPatterns = [
|
||||
/remove.*(?:accessibility|a11y)/i,
|
||||
/disable.*(?:security|auth|rate.*limit)/i,
|
||||
/skip.*(?:validation|verification)/i,
|
||||
/(accessibility|security|privacy)\s+(?:vs|versus|over|instead\s+of)\s+performance/i,
|
||||
/performance.*by.*(?:removing|disabling|skipping)/i,
|
||||
/(?:without|no)\s+(?:auth|security|validation)/i
|
||||
];
|
||||
|
||||
const hasValueConflict = tradeoffPatterns.some(p => p.test(user_message));
|
||||
|
||||
// 4. Detect schema/security operations
|
||||
const schemaKeywords = ['schema', 'model', 'collection', 'database', 'field', 'column', 'table', 'mongodb', 'mongoose'];
|
||||
const securityKeywords = ['auth', 'jwt', 'password', 'credential', 'token', 'session', 'cookie', 'secret'];
|
||||
|
||||
const isSchemaChange = schemaKeywords.some(k => user_message.toLowerCase().includes(k));
|
||||
const isSecurityChange = securityKeywords.some(k => user_message.toLowerCase().includes(k));
|
||||
|
||||
// 5. Detect multi-part instructions
|
||||
const multiPartIndicators = ['also', 'and also', 'additionally', 'furthermore', 'plus'];
|
||||
const isMultiPart = multiPartIndicators.some(indicator =>
|
||||
user_message.toLowerCase().includes(indicator)
|
||||
);
|
||||
|
||||
// 6. Build framework guidance
|
||||
let systemMessage = '';
|
||||
let frameworkRecommendation = null;
|
||||
|
||||
if (hasValueConflict && detectedValues.length > 0) {
|
||||
// Invoke PluralisticDeliberationOrchestrator
|
||||
const deliberation = PluralisticDeliberationOrchestrator.analyzeConflict(
|
||||
{
|
||||
type: 'value_conflict_prompt',
|
||||
description: user_message,
|
||||
context: 'user_prompt'
|
||||
},
|
||||
{
|
||||
sessionId,
|
||||
value_domains: detectedValues,
|
||||
source: 'prompt_analysis'
|
||||
}
|
||||
);
|
||||
|
||||
systemMessage += `\n🔍 FRAMEWORK ANALYSIS: Value conflict detected in user prompt\n`;
|
||||
systemMessage += `\nValues in tension: ${detectedValues.join(', ')}\n`;
|
||||
systemMessage += `Relevant rules: inst_016 (Privacy/Sovereignty), inst_017 (User Autonomy), inst_018 (Boundary Separation)\n`;
|
||||
|
||||
if (deliberation.recommendation) {
|
||||
systemMessage += `\nRecommendation: ${deliberation.recommendation}\n`;
|
||||
frameworkRecommendation = deliberation.recommendation;
|
||||
}
|
||||
|
||||
systemMessage += `\nNote: This is a values-based decision requiring deliberation. PluralisticDeliberationOrchestrator has been invoked and logged.\n`;
|
||||
}
|
||||
|
||||
if (isSchemaChange) {
|
||||
systemMessage += `\n🔍 FRAMEWORK ANALYSIS: Schema/database change detected\n`;
|
||||
systemMessage += `CrossReferenceValidator should be invoked during implementation.\n`;
|
||||
systemMessage += `Relevant: inst_064 (Schema changes require validation)\n`;
|
||||
}
|
||||
|
||||
if (isSecurityChange) {
|
||||
systemMessage += `\n🔍 FRAMEWORK ANALYSIS: Security-related operation detected\n`;
|
||||
systemMessage += `Elevated scrutiny required. Consider defense-in-depth implications.\n`;
|
||||
systemMessage += `Relevant: inst_072 (Defense-in-depth), inst_084 (Attack surface)\n`;
|
||||
}
|
||||
|
||||
if (classification.persistence === 'HIGH') {
|
||||
systemMessage += `\n🔍 FRAMEWORK ANALYSIS: High-persistence instruction detected\n`;
|
||||
systemMessage += `Quadrant: ${classification.quadrant}\n`;
|
||||
systemMessage += `Verification Level: ${classification.verification}\n`;
|
||||
systemMessage += `This instruction should be carefully validated before implementation.\n`;
|
||||
}
|
||||
|
||||
if (isMultiPart) {
|
||||
systemMessage += `\n🔍 FRAMEWORK ANALYSIS: Multi-part instruction detected\n`;
|
||||
systemMessage += `Consider analyzing each component separately for governance compliance.\n`;
|
||||
}
|
||||
|
||||
// Build comprehensive analysis object
|
||||
const frameworkAnalysis = {
|
||||
timestamp: new Date().toISOString(),
|
||||
sessionId,
|
||||
classification: {
|
||||
quadrant: classification.quadrant,
|
||||
persistence: classification.persistence,
|
||||
verification: classification.verification,
|
||||
explicitness: classification.explicitness
|
||||
},
|
||||
detectedValues,
|
||||
flags: {
|
||||
valueConflict: hasValueConflict,
|
||||
schemaChange: isSchemaChange,
|
||||
securityChange: isSecurityChange,
|
||||
multiPart: isMultiPart
|
||||
},
|
||||
recommendation: frameworkRecommendation,
|
||||
relevantRules: []
|
||||
};
|
||||
|
||||
// Add relevant rules based on detection
|
||||
if (hasValueConflict) frameworkAnalysis.relevantRules.push('inst_016', 'inst_017', 'inst_018');
|
||||
if (isSchemaChange) frameworkAnalysis.relevantRules.push('inst_064');
|
||||
if (isSecurityChange) frameworkAnalysis.relevantRules.push('inst_072', 'inst_084');
|
||||
|
||||
// PHASE 3.5: Store prompt analysis for cross-validation with actions
|
||||
try {
|
||||
const sessionStatePath = path.join(__dirname, '..', 'session-state.json');
|
||||
let sessionState = {};
|
||||
|
||||
if (fs.existsSync(sessionStatePath)) {
|
||||
sessionState = JSON.parse(fs.readFileSync(sessionStatePath, 'utf-8'));
|
||||
}
|
||||
|
||||
// Store latest prompt analysis
|
||||
if (!sessionState.promptAnalysis) {
|
||||
sessionState.promptAnalysis = {};
|
||||
}
|
||||
|
||||
sessionState.promptAnalysis.latest = frameworkAnalysis;
|
||||
sessionState.promptAnalysis.timestamp = new Date().toISOString();
|
||||
sessionState.promptAnalysis.messageCount = (sessionState.promptAnalysis.messageCount || 0) + 1;
|
||||
|
||||
fs.writeFileSync(sessionStatePath, JSON.stringify(sessionState, null, 2));
|
||||
} catch (err) {
|
||||
// Non-fatal: Continue even if storage fails
|
||||
console.error('[Prompt Analyzer] Failed to store analysis:', err.message);
|
||||
}
|
||||
|
||||
// Wait for async audit logging to complete
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
|
||||
await mongoose.disconnect();
|
||||
|
||||
// Return analysis to Claude
|
||||
outputResponse(frameworkAnalysis, systemMessage || null);
|
||||
process.exit(0);
|
||||
|
||||
} catch (err) {
|
||||
await mongoose.disconnect();
|
||||
|
||||
// Framework error - allow execution but log error
|
||||
outputResponse({
|
||||
error: 'Framework analysis error',
|
||||
message: err.message,
|
||||
stack: err.stack
|
||||
});
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Run hook
|
||||
main().catch(err => {
|
||||
outputResponse({
|
||||
error: 'Fatal error in prompt analyzer',
|
||||
message: err.message
|
||||
});
|
||||
process.exit(0);
|
||||
});
|
||||
|
|
@ -663,6 +663,20 @@ class BoundaryEnforcer {
|
|||
// Check for critical pressure requiring escalation
|
||||
const requiresEscalation = context.pressure_level === 'CRITICAL';
|
||||
|
||||
// PHASE 3: Build structured guidance
|
||||
const guidance = this._buildGuidance(
|
||||
'BLOCK',
|
||||
`Tractatus boundary ${primaryViolation.section} crossed: "${primaryViolation.principle}"`,
|
||||
'Human judgment required - this decision cannot be automated',
|
||||
'CRITICAL',
|
||||
['inst_001', 'inst_002', 'inst_003'], // Tractatus boundary rules
|
||||
{
|
||||
boundary: primaryViolation.boundary,
|
||||
section: primaryViolation.section,
|
||||
violations_count: violations.length
|
||||
}
|
||||
);
|
||||
|
||||
const result = {
|
||||
allowed: false,
|
||||
humanRequired: true,
|
||||
|
|
@ -681,6 +695,7 @@ class BoundaryEnforcer {
|
|||
violations,
|
||||
violated_boundaries: violations.map(v => v.boundary),
|
||||
action: 'REQUIRE_HUMAN_DECISION',
|
||||
guidance, // PHASE 3: Include guidance
|
||||
recommendation: 'Present options to human for decision',
|
||||
alternatives: this._generateAlternatives(primaryViolation.boundary, action), // Use 'alternatives' not 'suggested_alternatives'
|
||||
suggested_alternatives: this._generateAlternatives(primaryViolation.boundary, action), // Keep alias for backwards compatibility
|
||||
|
|
@ -707,6 +722,16 @@ class BoundaryEnforcer {
|
|||
}
|
||||
|
||||
_requireHumanApproval(domain, reason, action, context = {}) {
|
||||
// PHASE 3: Build structured guidance
|
||||
const guidance = this._buildGuidance(
|
||||
'REQUIRE_APPROVAL',
|
||||
`${domain} decisions require human approval: ${reason}`,
|
||||
'Present proposal to human for approval before proceeding',
|
||||
'HIGH',
|
||||
[], // Rule IDs would come from domain config
|
||||
{ domain, reason }
|
||||
);
|
||||
|
||||
return {
|
||||
allowed: false,
|
||||
humanRequired: true,
|
||||
|
|
@ -719,6 +744,7 @@ class BoundaryEnforcer {
|
|||
recommendation: 'Present proposal to human for approval',
|
||||
escalation_path: 'Requires human approval before proceeding',
|
||||
userPrompt: this._generateApprovalPrompt(domain, reason, action),
|
||||
guidance, // PHASE 3: Include guidance
|
||||
context: Object.keys(context).length > 0 ? context : undefined,
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
|
@ -745,6 +771,16 @@ class BoundaryEnforcer {
|
|||
this.stats.total_enforcements++;
|
||||
this.stats.allowed_count++;
|
||||
|
||||
// PHASE 3: Build structured guidance
|
||||
const guidance = this._buildGuidance(
|
||||
'ALLOW',
|
||||
`Action approved for ${domain} - no boundary violations detected`,
|
||||
'Proceed with action as planned',
|
||||
'INFO',
|
||||
[], // No specific rules triggered
|
||||
{ domain }
|
||||
);
|
||||
|
||||
const result = {
|
||||
allowed: true,
|
||||
humanRequired: false,
|
||||
|
|
@ -754,6 +790,7 @@ class BoundaryEnforcer {
|
|||
boundary: null, // Explicitly null when no boundary violation
|
||||
message: `Action approved for ${domain}`,
|
||||
action: 'PROCEED',
|
||||
guidance, // PHASE 3: Include guidance
|
||||
context: Object.keys(context).length > 0 ? context : undefined,
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
|
@ -838,6 +875,9 @@ class BoundaryEnforcer {
|
|||
sessionId: context.sessionId || 'boundary-enforcer-session'
|
||||
});
|
||||
|
||||
// PHASE 3: Include framework-backed decision indicator
|
||||
const frameworkBacked = !!(result.guidance && result.guidance.systemMessage);
|
||||
|
||||
// Audit asynchronously (don't block enforcement)
|
||||
this.memoryProxy.auditDecision({
|
||||
sessionId: context.sessionId || 'boundary-enforcer-session',
|
||||
|
|
@ -852,7 +892,10 @@ class BoundaryEnforcer {
|
|||
requirementType: result.requirementType,
|
||||
actionType: action.type || action.description,
|
||||
tractatus_section: result.tractatus_section,
|
||||
enforcement_decision: result.allowed ? 'ALLOWED' : 'BLOCKED'
|
||||
enforcement_decision: result.allowed ? 'ALLOWED' : 'BLOCKED',
|
||||
framework_backed_decision: frameworkBacked, // PHASE 3: Track framework participation
|
||||
guidance_provided: frameworkBacked,
|
||||
guidance_severity: result.guidance?.severity || null
|
||||
}
|
||||
}).catch(error => {
|
||||
logger.error('Failed to audit enforcement decision', {
|
||||
|
|
@ -862,6 +905,181 @@ class BoundaryEnforcer {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect schema changes in file path and content (Phase 2: Semantic Understanding)
|
||||
* @param {string} filePath - Path to file being modified
|
||||
* @param {string} content - Content of file or changes
|
||||
* @returns {Object} Schema change detection result
|
||||
*/
|
||||
detectSchemaChange(filePath, content = '') {
|
||||
// Pattern detection for schema modifications
|
||||
const schemaPatterns = [
|
||||
/new\s+Schema\(/i,
|
||||
/mongoose\.model\(/i,
|
||||
/mongoose\.Schema/i,
|
||||
/\.collection\(/i,
|
||||
/createCollection/i,
|
||||
/addField|removeField|modifyField/i,
|
||||
/\.schema\./i
|
||||
];
|
||||
|
||||
// File-based detection
|
||||
const isModelFile = /\.model\.js$/i.test(filePath);
|
||||
const isSchemaFile = /schema\.js$/i.test(filePath);
|
||||
|
||||
// Content-based detection
|
||||
const hasSchemaPattern = schemaPatterns.some(p => p.test(content));
|
||||
|
||||
// Detect sensitive collections (User, Auth, Payment, etc.)
|
||||
const sensitiveCollections = ['user', 'auth', 'credential', 'payment', 'transaction', 'session'];
|
||||
const isSensitiveCollection = sensitiveCollections.some(col =>
|
||||
filePath.toLowerCase().includes(col) || content.toLowerCase().includes(`'${col}`)
|
||||
);
|
||||
|
||||
// Determine severity
|
||||
let severity = 'ROUTINE';
|
||||
if (isSensitiveCollection) {
|
||||
severity = 'HIGH';
|
||||
} else if (isModelFile || isSchemaFile) {
|
||||
severity = 'MEDIUM';
|
||||
} else if (hasSchemaPattern) {
|
||||
severity = 'MEDIUM';
|
||||
}
|
||||
|
||||
return {
|
||||
isSchemaChange: isModelFile || isSchemaFile || hasSchemaPattern,
|
||||
severity,
|
||||
isSensitiveCollection,
|
||||
detectedPatterns: schemaPatterns.filter(p => p.test(content)).map(p => p.source),
|
||||
requiresValidation: isModelFile || isSchemaFile || isSensitiveCollection
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect security gradient level (Phase 2: Semantic Understanding)
|
||||
* Graduated security levels based on actual code content, not just file paths
|
||||
* @param {string} filePath - Path to file being modified
|
||||
* @param {string} content - Content of file or changes
|
||||
* @returns {string} Security gradient: CRITICAL | HIGH | ROUTINE | DOCUMENTATION
|
||||
*/
|
||||
detectSecurityGradient(filePath, content = '') {
|
||||
// CRITICAL: Core security mechanisms (passwords, keys, secrets)
|
||||
const criticalPatterns = [
|
||||
/password.*hash/i,
|
||||
/bcrypt|argon2|scrypt/i,
|
||||
/private.*key|secret.*key/i,
|
||||
/jwt.*secret/i,
|
||||
/\.env|process\.env\./i,
|
||||
/credential.*storage/i,
|
||||
/crypto\.createHash/i,
|
||||
/encryption.*key/i
|
||||
];
|
||||
|
||||
// HIGH: Authentication and authorization logic
|
||||
const highPatterns = [
|
||||
/authenticate|authorize/i,
|
||||
/jwt\.sign|jwt\.verify/i,
|
||||
/session\.create|session\.destroy/i,
|
||||
/cookie.*secure|httpOnly/i,
|
||||
/req\.user|req\.session/i,
|
||||
/access.*control|permission/i,
|
||||
/role.*check|hasRole/i
|
||||
];
|
||||
|
||||
// ROUTINE: Standard middleware, validation, logging
|
||||
const routinePatterns = [
|
||||
/middleware/i,
|
||||
/validation|validate/i,
|
||||
/logger|logging/i,
|
||||
/error.*handler/i,
|
||||
/sanitize|escape/i
|
||||
];
|
||||
|
||||
// DOCUMENTATION: Comments, docs, examples
|
||||
const documentationPatterns = [
|
||||
/^\/\//m, // Single-line comment at start
|
||||
/^\/\*/m, // Multi-line comment at start
|
||||
/\*\s*@param/, // JSDoc parameter
|
||||
/\*\s*@returns/, // JSDoc return
|
||||
/README|CHANGELOG|\.md$/i,
|
||||
/example|sample|demo/i
|
||||
];
|
||||
|
||||
// Check patterns in order of severity (highest first)
|
||||
if (criticalPatterns.some(p => p.test(content))) {
|
||||
return 'CRITICAL';
|
||||
}
|
||||
|
||||
if (highPatterns.some(p => p.test(content) || p.test(filePath))) {
|
||||
return 'HIGH';
|
||||
}
|
||||
|
||||
// Documentation check: if majority of content is comments/docs
|
||||
const documentationMatches = documentationPatterns.filter(p => p.test(content)).length;
|
||||
if (documentationMatches >= 2 || /^\/\*\*/.test(content.trim())) {
|
||||
return 'DOCUMENTATION';
|
||||
}
|
||||
|
||||
if (routinePatterns.some(p => p.test(content) || p.test(filePath))) {
|
||||
return 'ROUTINE';
|
||||
}
|
||||
|
||||
// Default: if file path suggests security but content unclear
|
||||
if (/auth|security|credential|jwt|password|session/i.test(filePath)) {
|
||||
return 'HIGH'; // Err on side of caution
|
||||
}
|
||||
|
||||
return 'ROUTINE';
|
||||
}
|
||||
|
||||
/**
|
||||
* PHASE 3: Build structured guidance for framework-to-Claude communication
|
||||
*
|
||||
* @param {string} decision - ALLOW | BLOCK | REQUIRE_APPROVAL | REQUIRE_REVIEW
|
||||
* @param {string} summary - One-line human-readable summary
|
||||
* @param {string} recommendation - Actionable next step
|
||||
* @param {string} severity - CRITICAL | HIGH | MEDIUM | LOW | INFO
|
||||
* @param {Array} ruleIds - Relevant governance rule IDs
|
||||
* @param {Object} metadata - Additional context
|
||||
* @returns {Object} Structured guidance object
|
||||
*/
|
||||
_buildGuidance(decision, summary, recommendation, severity, ruleIds = [], metadata = {}) {
|
||||
const severityEmojis = {
|
||||
'CRITICAL': '🚨',
|
||||
'HIGH': '⚠️',
|
||||
'MEDIUM': '📋',
|
||||
'LOW': 'ℹ️',
|
||||
'INFO': '💡'
|
||||
};
|
||||
|
||||
const emoji = severityEmojis[severity] || 'ℹ️';
|
||||
|
||||
// Build systemMessage for hook injection into Claude's context
|
||||
let systemMessage = `\n${emoji} FRAMEWORK GUIDANCE (BoundaryEnforcer):\n`;
|
||||
systemMessage += `Decision: ${decision}\n`;
|
||||
systemMessage += `${summary}\n`;
|
||||
|
||||
if (recommendation) {
|
||||
systemMessage += `\nRecommendation: ${recommendation}\n`;
|
||||
}
|
||||
|
||||
if (ruleIds.length > 0) {
|
||||
systemMessage += `\nRelevant Rules: ${ruleIds.join(', ')}\n`;
|
||||
}
|
||||
|
||||
return {
|
||||
summary,
|
||||
systemMessage,
|
||||
recommendation,
|
||||
severity,
|
||||
framework_service: 'BoundaryEnforcer',
|
||||
rule_ids: ruleIds,
|
||||
decision_type: decision,
|
||||
metadata,
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enforcement statistics
|
||||
* @returns {Object} Statistics object
|
||||
|
|
|
|||
|
|
@ -445,11 +445,22 @@ class CrossReferenceValidator {
|
|||
this.stats.total_validations++;
|
||||
this.stats.approvals++;
|
||||
|
||||
// PHASE 3: Build structured guidance
|
||||
const guidance = this._buildGuidance(
|
||||
'APPROVE',
|
||||
message,
|
||||
'Action aligned with user instructions - proceed',
|
||||
'INFO',
|
||||
[], // No specific rules violated
|
||||
{ conflicts_count: conflicts.length }
|
||||
);
|
||||
|
||||
return {
|
||||
status: VALIDATION_STATUS.APPROVED,
|
||||
message,
|
||||
conflicts,
|
||||
action: 'PROCEED',
|
||||
guidance, // PHASE 3: Include guidance
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
|
@ -463,14 +474,31 @@ class CrossReferenceValidator {
|
|||
const primaryConflict = conflicts[0];
|
||||
const timeAgo = this._formatTimeAgo(primaryConflict.instruction.timestamp);
|
||||
|
||||
const message = `Potential conflict in parameter '${primaryConflict.parameter}': ` +
|
||||
`action uses '${primaryConflict.actionValue}' but user instruction ` +
|
||||
`specified '${primaryConflict.instructionValue}' (${timeAgo} ago)`;
|
||||
|
||||
// PHASE 3: Build structured guidance
|
||||
const guidance = this._buildGuidance(
|
||||
'WARN',
|
||||
message,
|
||||
`Consider using '${primaryConflict.instructionValue}' instead`,
|
||||
'MEDIUM',
|
||||
[primaryConflict.instruction.id || 'user_instruction'],
|
||||
{
|
||||
parameter: primaryConflict.parameter,
|
||||
action_value: primaryConflict.actionValue,
|
||||
instruction_value: primaryConflict.instructionValue
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
status: VALIDATION_STATUS.WARNING,
|
||||
message: `Potential conflict in parameter '${primaryConflict.parameter}': ` +
|
||||
`action uses '${primaryConflict.actionValue}' but user instruction ` +
|
||||
`specified '${primaryConflict.instructionValue}' (${timeAgo} ago)`,
|
||||
message,
|
||||
conflicts,
|
||||
action: 'NOTIFY_USER',
|
||||
recommendation: `Consider using '${primaryConflict.instructionValue}' instead`,
|
||||
guidance, // PHASE 3: Include guidance
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
|
@ -484,17 +512,35 @@ class CrossReferenceValidator {
|
|||
const primaryConflict = conflicts[0];
|
||||
const timeAgo = this._formatTimeAgo(primaryConflict.instruction.timestamp);
|
||||
|
||||
const message = `CRITICAL CONFLICT: Action parameter '${primaryConflict.parameter}' ` +
|
||||
`uses '${primaryConflict.actionValue}' but user explicitly specified ` +
|
||||
`'${primaryConflict.instructionValue}' ${timeAgo} ago`;
|
||||
|
||||
// PHASE 3: Build structured guidance
|
||||
const guidance = this._buildGuidance(
|
||||
'REJECT',
|
||||
message,
|
||||
'Verify with user before proceeding - explicit instruction conflict detected',
|
||||
'CRITICAL',
|
||||
[primaryConflict.instruction.id || 'user_instruction'],
|
||||
{
|
||||
parameter: primaryConflict.parameter,
|
||||
action_value: primaryConflict.actionValue,
|
||||
instruction_value: primaryConflict.instructionValue,
|
||||
instruction_quote: primaryConflict.instruction.text
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
status: VALIDATION_STATUS.REJECTED,
|
||||
message: `CRITICAL CONFLICT: Action parameter '${primaryConflict.parameter}' ` +
|
||||
`uses '${primaryConflict.actionValue}' but user explicitly specified ` +
|
||||
`'${primaryConflict.instructionValue}' ${timeAgo} ago`,
|
||||
message,
|
||||
conflicts,
|
||||
action: 'REQUEST_CLARIFICATION',
|
||||
required_action: 'REQUEST_CLARIFICATION',
|
||||
recommendation: `Verify with user before proceeding`,
|
||||
instructionQuote: primaryConflict.instruction.text,
|
||||
requiredValue: primaryConflict.instructionValue,
|
||||
guidance, // PHASE 3: Include guidance
|
||||
timestamp: new Date(),
|
||||
userPrompt: `I noticed a conflict:\n\n` +
|
||||
`You instructed: "${primaryConflict.instruction.text}"\n` +
|
||||
|
|
@ -504,10 +550,21 @@ class CrossReferenceValidator {
|
|||
}
|
||||
|
||||
_escalateResult(message) {
|
||||
// PHASE 3: Build structured guidance
|
||||
const guidance = this._buildGuidance(
|
||||
'ESCALATE',
|
||||
message,
|
||||
'Human review required - complexity exceeds framework capabilities',
|
||||
'HIGH',
|
||||
[],
|
||||
{}
|
||||
);
|
||||
|
||||
return {
|
||||
status: VALIDATION_STATUS.ESCALATE,
|
||||
message,
|
||||
action: 'REQUIRE_HUMAN_REVIEW',
|
||||
guidance, // PHASE 3: Include guidance
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
|
@ -567,6 +624,9 @@ class CrossReferenceValidator {
|
|||
?.filter(c => c.severity === CONFLICT_SEVERITY.CRITICAL)
|
||||
.map(c => c.instruction?.text || c.parameter) || [];
|
||||
|
||||
// PHASE 3: Include framework-backed decision indicator
|
||||
const frameworkBacked = !!(decision.guidance && decision.guidance.systemMessage);
|
||||
|
||||
// Audit asynchronously (don't block validation)
|
||||
this.memoryProxy.auditDecision({
|
||||
sessionId: context.sessionId || 'validator-service',
|
||||
|
|
@ -582,6 +642,9 @@ class CrossReferenceValidator {
|
|||
critical_conflicts: violations.length,
|
||||
relevant_instructions: relevantInstructions.length,
|
||||
validation_action: decision.action,
|
||||
framework_backed_decision: frameworkBacked, // PHASE 3: Track framework participation
|
||||
guidance_provided: frameworkBacked,
|
||||
guidance_severity: decision.guidance?.severity || null,
|
||||
conflict_details: decision.conflicts?.slice(0, 3).map(c => ({
|
||||
parameter: c.parameter,
|
||||
severity: c.severity,
|
||||
|
|
@ -597,10 +660,250 @@ class CrossReferenceValidator {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate schema changes against governance rules (Phase 2: Semantic Understanding)
|
||||
* @param {Object} action - Action to validate
|
||||
* @param {Object} context - Validation context
|
||||
* @returns {Promise<Object>} Validation result
|
||||
*/
|
||||
async validateSchemaChange(action, context = {}) {
|
||||
try {
|
||||
// Find all schema-related instructions
|
||||
const schemaRules = this.governanceRules.filter(rule =>
|
||||
rule.text.toLowerCase().includes('schema') ||
|
||||
rule.text.toLowerCase().includes('database') ||
|
||||
rule.text.toLowerCase().includes('model') ||
|
||||
rule.text.toLowerCase().includes('collection') ||
|
||||
rule.category === 'data_architecture' ||
|
||||
rule.quadrant === 'SYSTEM'
|
||||
);
|
||||
|
||||
// Find rules about specific sensitive data types
|
||||
const sensitiveDataRules = this.governanceRules.filter(rule =>
|
||||
rule.text.toLowerCase().includes('user') ||
|
||||
rule.text.toLowerCase().includes('auth') ||
|
||||
rule.text.toLowerCase().includes('credential') ||
|
||||
rule.text.toLowerCase().includes('privacy') ||
|
||||
rule.text.toLowerCase().includes('personal data')
|
||||
);
|
||||
|
||||
// Combine relevant rules
|
||||
const relevantRules = [...schemaRules, ...sensitiveDataRules];
|
||||
|
||||
// Check for conflicts with action
|
||||
const conflicts = [];
|
||||
for (const rule of relevantRules) {
|
||||
// Simple conflict detection: if rule says "never" or "always" and action contradicts
|
||||
const ruleText = rule.text.toLowerCase();
|
||||
const actionDesc = (action.description || action.type || '').toLowerCase();
|
||||
|
||||
// Detect potential conflicts
|
||||
if (ruleText.includes('never') && actionDesc.includes('modify')) {
|
||||
conflicts.push({
|
||||
rule,
|
||||
severity: rule.persistence === 'HIGH' ? 'CRITICAL' : 'WARNING',
|
||||
reason: 'Schema modification may conflict with protection rule'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for approval requirements
|
||||
if ((ruleText.includes('approval') || ruleText.includes('human')) &&
|
||||
context.automated_approval) {
|
||||
conflicts.push({
|
||||
rule,
|
||||
severity: 'HIGH',
|
||||
reason: 'Human approval required for schema changes'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if action is allowed
|
||||
const criticalConflicts = conflicts.filter(c => c.severity === 'CRITICAL');
|
||||
const allowed = criticalConflicts.length === 0;
|
||||
|
||||
// Determine if human approval is required
|
||||
const requiresApproval = conflicts.some(c =>
|
||||
c.rule.persistence === 'HIGH' ||
|
||||
c.rule.quadrant === 'STRATEGIC' ||
|
||||
c.severity === 'CRITICAL'
|
||||
);
|
||||
|
||||
const recommendation = this._getSchemaRecommendation(conflicts, requiresApproval);
|
||||
|
||||
// PHASE 3: Build structured guidance for schema validation
|
||||
const severity = criticalConflicts.length > 0 ? 'CRITICAL' :
|
||||
requiresApproval ? 'HIGH' :
|
||||
conflicts.length > 0 ? 'MEDIUM' : 'INFO';
|
||||
|
||||
const decision = criticalConflicts.length > 0 ? 'REJECT' :
|
||||
requiresApproval ? 'REQUIRE_APPROVAL' :
|
||||
allowed ? 'APPROVE' : 'WARN';
|
||||
|
||||
const guidance = this._buildGuidance(
|
||||
decision,
|
||||
`Schema validation: ${recommendation}`,
|
||||
recommendation,
|
||||
severity,
|
||||
relevantRules.map(r => r.id || r.ruleId).slice(0, 5), // Limit to 5 rules
|
||||
{
|
||||
schema_change: true,
|
||||
sensitive_collection: context.sensitive_collection || false,
|
||||
conflicts_count: conflicts.length,
|
||||
critical_conflicts_count: criticalConflicts.length
|
||||
}
|
||||
);
|
||||
|
||||
const result = {
|
||||
allowed,
|
||||
conflicts,
|
||||
criticalConflicts: criticalConflicts.length,
|
||||
warningConflicts: conflicts.filter(c => c.severity === 'WARNING').length,
|
||||
requiresApproval,
|
||||
relevantRules: relevantRules.map(r => r.id || r.ruleId),
|
||||
recommendation,
|
||||
guidance // PHASE 3: Include guidance
|
||||
};
|
||||
|
||||
// Log validation to audit
|
||||
this._auditSchemaValidation(action, result, context);
|
||||
|
||||
return result;
|
||||
|
||||
} catch (error) {
|
||||
logger.error('[CrossReferenceValidator] Schema validation error:', {
|
||||
error: error.message,
|
||||
action: action.description
|
||||
});
|
||||
|
||||
return {
|
||||
allowed: false,
|
||||
conflicts: [],
|
||||
error: error.message,
|
||||
recommendation: 'Schema validation failed - manual review recommended'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recommendation based on schema validation conflicts
|
||||
* @private
|
||||
*/
|
||||
_getSchemaRecommendation(conflicts, requiresApproval) {
|
||||
if (conflicts.length === 0) {
|
||||
return 'Schema change appears compliant with governance rules';
|
||||
}
|
||||
|
||||
const criticalCount = conflicts.filter(c => c.severity === 'CRITICAL').length;
|
||||
|
||||
if (criticalCount > 0) {
|
||||
return `BLOCK: ${criticalCount} critical conflict(s) detected. Human review required.`;
|
||||
}
|
||||
|
||||
if (requiresApproval) {
|
||||
return 'CAUTION: Human approval required before proceeding with schema change';
|
||||
}
|
||||
|
||||
return `WARNING: ${conflicts.length} potential conflict(s) detected. Review recommended.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Audit schema validation decision
|
||||
* @private
|
||||
*/
|
||||
async _auditSchemaValidation(action, result, context) {
|
||||
if (!this.memoryProxyInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
const violations = result.conflicts
|
||||
.filter(c => c.severity === 'CRITICAL')
|
||||
.map(c => c.rule.id || c.rule.ruleId);
|
||||
|
||||
// PHASE 3: Include framework-backed decision indicator
|
||||
const frameworkBacked = !!(result.guidance && result.guidance.systemMessage);
|
||||
|
||||
this.memoryProxy.auditDecision({
|
||||
sessionId: context.sessionId || 'schema-validator',
|
||||
action: 'schema_change_validation',
|
||||
service: 'CrossReferenceValidator',
|
||||
rulesChecked: result.relevantRules,
|
||||
violations,
|
||||
allowed: result.allowed,
|
||||
metadata: {
|
||||
action_description: action.description,
|
||||
validation_type: 'schema_change',
|
||||
critical_conflicts: result.criticalConflicts,
|
||||
warning_conflicts: result.warningConflicts,
|
||||
requires_approval: result.requiresApproval,
|
||||
recommendation: result.recommendation,
|
||||
framework_backed_decision: frameworkBacked, // PHASE 3: Track framework participation
|
||||
guidance_provided: frameworkBacked,
|
||||
guidance_severity: result.guidance?.severity || null,
|
||||
conflict_details: result.conflicts.slice(0, 5).map(c => ({
|
||||
rule_id: c.rule.id || c.rule.ruleId,
|
||||
severity: c.severity,
|
||||
reason: c.reason
|
||||
}))
|
||||
}
|
||||
}).catch(error => {
|
||||
logger.error('[CrossReferenceValidator] Failed to audit schema validation', {
|
||||
error: error.message
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get validation statistics
|
||||
* @returns {Object} Statistics object
|
||||
*/
|
||||
/**
|
||||
* PHASE 3: Build structured guidance for framework-to-Claude communication
|
||||
*
|
||||
* @param {string} decision - APPROVE | WARN | REJECT | ESCALATE
|
||||
* @param {string} summary - One-line human-readable summary
|
||||
* @param {string} recommendation - Actionable next step
|
||||
* @param {string} severity - CRITICAL | HIGH | MEDIUM | LOW | INFO
|
||||
* @param {Array} ruleIds - Relevant governance rule IDs
|
||||
* @param {Object} metadata - Additional context
|
||||
* @returns {Object} Structured guidance object
|
||||
*/
|
||||
_buildGuidance(decision, summary, recommendation, severity, ruleIds = [], metadata = {}) {
|
||||
const severityEmojis = {
|
||||
'CRITICAL': '🚨',
|
||||
'HIGH': '⚠️',
|
||||
'MEDIUM': '📋',
|
||||
'LOW': 'ℹ️',
|
||||
'INFO': '💡'
|
||||
};
|
||||
|
||||
const emoji = severityEmojis[severity] || 'ℹ️';
|
||||
|
||||
// Build systemMessage for hook injection into Claude's context
|
||||
let systemMessage = `\n${emoji} FRAMEWORK GUIDANCE (CrossReferenceValidator):\n`;
|
||||
systemMessage += `Decision: ${decision}\n`;
|
||||
systemMessage += `${summary}\n`;
|
||||
|
||||
if (recommendation) {
|
||||
systemMessage += `\nRecommendation: ${recommendation}\n`;
|
||||
}
|
||||
|
||||
if (ruleIds.length > 0) {
|
||||
systemMessage += `\nRelevant Rules: ${ruleIds.join(', ')}\n`;
|
||||
}
|
||||
|
||||
return {
|
||||
summary,
|
||||
systemMessage,
|
||||
recommendation,
|
||||
severity,
|
||||
framework_service: 'CrossReferenceValidator',
|
||||
rule_ids: ruleIds,
|
||||
decision_type: decision,
|
||||
metadata,
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
getStats() {
|
||||
return {
|
||||
...this.stats,
|
||||
|
|
|
|||
|
|
@ -211,6 +211,37 @@ class MetacognitiveVerifier {
|
|||
context
|
||||
);
|
||||
|
||||
const failedChecks = criticalFailures.map(cf => cf.dimension);
|
||||
const recommendations = this._generateRecommendations(scores, criticalFailures, pressureAnalysis);
|
||||
|
||||
// PHASE 3: Build structured guidance
|
||||
const severity = decision === 'BLOCK' ? 'CRITICAL' :
|
||||
decision === 'REQUEST_CONFIRMATION' ? 'HIGH' :
|
||||
adjustedConfidence < 0.7 ? 'MEDIUM' : 'INFO';
|
||||
|
||||
const summary = decision === 'BLOCK'
|
||||
? 'Verification FAILED - action blocked'
|
||||
: decision === 'REQUEST_CONFIRMATION'
|
||||
? `Verification uncertain (${(adjustedConfidence * 100).toFixed(1)}%) - confirmation recommended`
|
||||
: `Verification passed (${(adjustedConfidence * 100).toFixed(1)}%)`;
|
||||
|
||||
const recommendation = decision !== 'PROCEED' && recommendations.length > 0
|
||||
? recommendations[0]
|
||||
: 'Proceed with action as planned';
|
||||
|
||||
const guidance = this._buildGuidance(
|
||||
decision,
|
||||
summary,
|
||||
recommendation,
|
||||
severity,
|
||||
adjustedConfidence,
|
||||
{
|
||||
failed_checks: failedChecks,
|
||||
pressure_level: pressureAnalysis.pressureName,
|
||||
critical_failures_count: criticalFailures.length
|
||||
}
|
||||
);
|
||||
|
||||
const verification = {
|
||||
confidence: adjustedConfidence,
|
||||
originalConfidence: confidence,
|
||||
|
|
@ -232,20 +263,17 @@ class MetacognitiveVerifier {
|
|||
threshold_adjusted: pressureAnalysis.pressureName !== 'NORMAL' || context.pressure_level !== 'NORMAL' && context.pressure_level !== undefined,
|
||||
required_confidence: (pressureAnalysis.pressureName === 'CRITICAL' || context.pressure_level === 'CRITICAL') ? 0.8 : 0.6,
|
||||
requires_confirmation: decision === 'REQUEST_CONFIRMATION',
|
||||
recommendations: this._generateRecommendations(
|
||||
scores,
|
||||
criticalFailures,
|
||||
pressureAnalysis
|
||||
),
|
||||
recommendations,
|
||||
decision,
|
||||
reason: decision === 'BLOCK' && (pressureAnalysis.pressureLevel >= 4 || context.pressure_level === 'DANGEROUS')
|
||||
? 'Operation blocked: pressure too high for safe execution'
|
||||
: (decision !== 'PROCEED' ? this._getDecisionReason(decision, scores, criticalFailures) : undefined),
|
||||
analysis: {
|
||||
failed_checks: criticalFailures.map(cf => cf.dimension),
|
||||
recommendations: this._generateRecommendations(scores, criticalFailures, pressureAnalysis)
|
||||
failed_checks: failedChecks,
|
||||
recommendations
|
||||
},
|
||||
suggestions: decision !== 'PROCEED' ? this._generateSuggestions(scores, criticalFailures) : undefined,
|
||||
guidance, // PHASE 3: Include guidance
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
|
|
@ -1023,6 +1051,9 @@ class MetacognitiveVerifier {
|
|||
?.filter(f => f.severity === 'CRITICAL')
|
||||
.map(f => f.dimension) || [];
|
||||
|
||||
// PHASE 3: Include framework-backed decision indicator
|
||||
const frameworkBacked = !!(verification.guidance && verification.guidance.systemMessage);
|
||||
|
||||
this.memoryProxy.auditDecision({
|
||||
sessionId: context.sessionId || 'verifier-service',
|
||||
action: 'metacognitive_verification',
|
||||
|
|
@ -1038,6 +1069,9 @@ class MetacognitiveVerifier {
|
|||
level: verification.level,
|
||||
pressure_level: verification.pressureLevel,
|
||||
pressure_adjustment: verification.pressureAdjustment,
|
||||
framework_backed_decision: frameworkBacked, // PHASE 3: Track framework participation
|
||||
guidance_provided: frameworkBacked,
|
||||
guidance_severity: verification.guidance?.severity || null,
|
||||
checks: {
|
||||
alignment: verification.checks.alignment.passed,
|
||||
coherence: verification.checks.coherence.passed,
|
||||
|
|
@ -1254,6 +1288,55 @@ class MetacognitiveVerifier {
|
|||
* Get verification statistics
|
||||
* @returns {Object} Statistics object
|
||||
*/
|
||||
/**
|
||||
* PHASE 3: Build structured guidance for framework-to-Claude communication
|
||||
*
|
||||
* @param {string} decision - PROCEED | BLOCK | REQUEST_CONFIRMATION
|
||||
* @param {string} summary - One-line human-readable summary
|
||||
* @param {string} recommendation - Actionable next step
|
||||
* @param {string} severity - CRITICAL | HIGH | MEDIUM | LOW | INFO
|
||||
* @param {number} confidence - Confidence score (0-1)
|
||||
* @param {Object} metadata - Additional context
|
||||
* @returns {Object} Structured guidance object
|
||||
*/
|
||||
_buildGuidance(decision, summary, recommendation, severity, confidence, metadata = {}) {
|
||||
const severityEmojis = {
|
||||
'CRITICAL': '🚨',
|
||||
'HIGH': '⚠️',
|
||||
'MEDIUM': '📋',
|
||||
'LOW': 'ℹ️',
|
||||
'INFO': '💡'
|
||||
};
|
||||
|
||||
const emoji = severityEmojis[severity] || 'ℹ️';
|
||||
|
||||
// Build systemMessage for hook injection into Claude's context
|
||||
let systemMessage = `\n${emoji} FRAMEWORK GUIDANCE (MetacognitiveVerifier):\n`;
|
||||
systemMessage += `Decision: ${decision}\n`;
|
||||
systemMessage += `Confidence: ${(confidence * 100).toFixed(1)}%\n`;
|
||||
systemMessage += `${summary}\n`;
|
||||
|
||||
if (recommendation) {
|
||||
systemMessage += `\nRecommendation: ${recommendation}\n`;
|
||||
}
|
||||
|
||||
if (metadata.failed_checks && metadata.failed_checks.length > 0) {
|
||||
systemMessage += `\nFailed Checks: ${metadata.failed_checks.join(', ')}\n`;
|
||||
}
|
||||
|
||||
return {
|
||||
summary,
|
||||
systemMessage,
|
||||
recommendation,
|
||||
severity,
|
||||
confidence_score: confidence,
|
||||
framework_service: 'MetacognitiveVerifier',
|
||||
decision_type: decision,
|
||||
metadata,
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
getStats() {
|
||||
return {
|
||||
...this.stats,
|
||||
|
|
|
|||
|
|
@ -192,6 +192,30 @@ class PluralisticDeliberationOrchestrator {
|
|||
this.stats.total_deliberations++;
|
||||
this.stats.by_urgency[urgency]++;
|
||||
|
||||
// PHASE 3: Build structured guidance
|
||||
const frameworkNames = frameworksInTension.map(f => f.name || f);
|
||||
const severity = urgency === 'CRITICAL' ? 'CRITICAL' :
|
||||
urgency === 'HIGH' ? 'HIGH' : 'MEDIUM';
|
||||
|
||||
const summary = frameworksInTension.length > 0
|
||||
? `Value conflict detected: ${frameworkNames.join(', ')} in tension`
|
||||
: 'Potential value conflict - human deliberation required';
|
||||
|
||||
const recommendation = `${this.urgencyTiers[urgency].process} (${this.urgencyTiers[urgency].timeframe}) - Human decision required per Tractatus 12.1`;
|
||||
|
||||
const guidance = this._buildGuidance(
|
||||
summary,
|
||||
recommendation,
|
||||
severity,
|
||||
frameworkNames,
|
||||
{
|
||||
urgency_tier: urgency,
|
||||
trade_offs_count: valueTradeOffs.length,
|
||||
stakeholders_count: affectedStakeholders.length,
|
||||
precedents_count: relevantPrecedents.length
|
||||
}
|
||||
);
|
||||
|
||||
const analysis = {
|
||||
moral_frameworks_in_tension: frameworksInTension,
|
||||
value_trade_offs: valueTradeOffs,
|
||||
|
|
@ -203,6 +227,7 @@ class PluralisticDeliberationOrchestrator {
|
|||
requires_human_approval: true, // Always true per TRA-OPS-0002
|
||||
ai_role: 'FACILITATE_ONLY',
|
||||
human_role: 'DECIDE',
|
||||
guidance, // PHASE 3: Include guidance
|
||||
analysis_timestamp: new Date()
|
||||
};
|
||||
|
||||
|
|
@ -533,6 +558,9 @@ class PluralisticDeliberationOrchestrator {
|
|||
sessionId: context.sessionId || 'deliberation-orchestrator'
|
||||
});
|
||||
|
||||
// PHASE 3: Include framework-backed decision indicator
|
||||
const frameworkBacked = !!(analysis.guidance && analysis.guidance.systemMessage);
|
||||
|
||||
// Audit asynchronously (don't block analysis)
|
||||
this.memoryProxy.auditDecision({
|
||||
sessionId: context.sessionId || 'deliberation-orchestrator',
|
||||
|
|
@ -552,7 +580,10 @@ class PluralisticDeliberationOrchestrator {
|
|||
precedents_found: analysis.relevant_precedents.length,
|
||||
requires_human_approval: analysis.requires_human_approval,
|
||||
ai_role: analysis.ai_role,
|
||||
human_role: analysis.human_role
|
||||
human_role: analysis.human_role,
|
||||
framework_backed_decision: frameworkBacked, // PHASE 3: Track framework participation
|
||||
guidance_provided: frameworkBacked,
|
||||
guidance_severity: analysis.guidance?.severity || null
|
||||
}
|
||||
}).catch(error => {
|
||||
logger.error('[PluralisticDeliberationOrchestrator] Failed to audit deliberation', {
|
||||
|
|
@ -833,6 +864,53 @@ class PluralisticDeliberationOrchestrator {
|
|||
* Get deliberation statistics
|
||||
* @returns {Object} Statistics object
|
||||
*/
|
||||
/**
|
||||
* PHASE 3: Build structured guidance for framework-to-Claude communication
|
||||
*
|
||||
* @param {string} summary - One-line human-readable summary
|
||||
* @param {string} recommendation - Actionable next step
|
||||
* @param {string} severity - CRITICAL | HIGH | MEDIUM | LOW | INFO
|
||||
* @param {Array} frameworks - Moral frameworks in tension
|
||||
* @param {Object} metadata - Additional context
|
||||
* @returns {Object} Structured guidance object
|
||||
*/
|
||||
_buildGuidance(summary, recommendation, severity, frameworks = [], metadata = {}) {
|
||||
const severityEmojis = {
|
||||
'CRITICAL': '🚨',
|
||||
'HIGH': '⚠️',
|
||||
'MEDIUM': '📋',
|
||||
'LOW': 'ℹ️',
|
||||
'INFO': '💡'
|
||||
};
|
||||
|
||||
const emoji = severityEmojis[severity] || 'ℹ️';
|
||||
|
||||
// Build systemMessage for hook injection into Claude's context
|
||||
let systemMessage = `\n${emoji} FRAMEWORK GUIDANCE (PluralisticDeliberationOrchestrator):\n`;
|
||||
systemMessage += `${summary}\n`;
|
||||
|
||||
if (frameworks.length > 0) {
|
||||
systemMessage += `\nFrameworks in Tension: ${frameworks.join(', ')}\n`;
|
||||
}
|
||||
|
||||
if (recommendation) {
|
||||
systemMessage += `\nRecommendation: ${recommendation}\n`;
|
||||
}
|
||||
|
||||
systemMessage += `\nAI Role: FACILITATE ONLY | Human Role: DECIDE\n`;
|
||||
|
||||
return {
|
||||
summary,
|
||||
systemMessage,
|
||||
recommendation,
|
||||
severity,
|
||||
framework_service: 'PluralisticDeliberationOrchestrator',
|
||||
frameworks_in_tension: frameworks,
|
||||
metadata,
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
getStats() {
|
||||
return {
|
||||
...this.stats,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue