tractatus/scripts/framework-components/CrossReferenceValidator.js
TheFlow ac2db33732 fix(submissions): restructure Economist package and fix article display
- Create Economist SubmissionTracking package correctly:
  * mainArticle = full blog post content
  * coverLetter = 216-word SIR— letter
  * Links to blog post via blogPostId
- Archive 'Letter to The Economist' from blog posts (it's the cover letter)
- Fix date display on article cards (use published_at)
- Target publication already displaying via blue badge

Database changes:
- Make blogPostId optional in SubmissionTracking model
- Economist package ID: 68fa85ae49d4900e7f2ecd83
- Le Monde package ID: 68fa2abd2e6acd5691932150

Next: Enhanced modal with tabs, validation, export

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 08:47:42 +13:00

310 lines
9.3 KiB
JavaScript
Executable file

#!/usr/bin/env node
/**
* CrossReferenceValidator - Active Enforcement Module
*
* Validates proposed actions against instruction history to prevent
* pattern recognition bias (the 27027 problem).
*
* This module can be called from:
* - Hook validators (automatic enforcement)
* - Pre-action-check script (manual invocation)
* - Directly by AI in code (voluntary invocation)
*
* Copyright 2025 Tractatus Project
* Licensed under Apache License 2.0
*/
const fs = require('fs');
const path = require('path');
const INSTRUCTION_HISTORY_PATH = path.join(__dirname, '../../.claude/instruction-history.json');
const SESSION_STATE_PATH = path.join(__dirname, '../../.claude/session-state.json');
class CrossReferenceValidator {
constructor(options = {}) {
this.silent = options.silent || false;
this.instructions = null;
this.loadInstructions();
}
loadInstructions() {
try {
if (!fs.existsSync(INSTRUCTION_HISTORY_PATH)) {
this.log('warning', 'Instruction history not found');
this.instructions = [];
return;
}
const data = JSON.parse(fs.readFileSync(INSTRUCTION_HISTORY_PATH, 'utf8'));
this.instructions = data.instructions || [];
this.log('info', `Loaded ${this.instructions.length} instructions`);
} catch (error) {
this.log('error', `Failed to load instructions: ${error.message}`);
this.instructions = [];
}
}
log(level, message) {
if (this.silent && level !== 'error') return;
const colors = {
info: '\x1b[36m',
warning: '\x1b[33m',
error: '\x1b[31m',
success: '\x1b[32m',
reset: '\x1b[0m'
};
const prefix = {
info: '[CrossReferenceValidator]',
warning: '[⚠ CrossReferenceValidator]',
error: '[✗ CrossReferenceValidator]',
success: '[✓ CrossReferenceValidator]'
}[level];
console.log(`${colors[level]}${prefix} ${message}${colors.reset}`);
}
/**
* Validate a file edit action
*/
validateFileEdit(filePath, oldString, newString) {
const relevantInstructions = this.findRelevantInstructions('file-edit', filePath);
if (relevantInstructions.length === 0) {
return { passed: true, relevantInstructions: [] };
}
this.log('info', `Found ${relevantInstructions.length} relevant instructions for file edit`);
// Check for conflicts
const conflicts = [];
relevantInstructions.forEach(inst => {
// Check CSP instructions (inst_008)
if (inst.id === 'inst_008' && (filePath.endsWith('.html') || filePath.endsWith('.js'))) {
const cspPatterns = [
{ name: 'inline event handlers', regex: /\son\w+\s*=\s*["'][^"']*["']/gi },
{ name: 'inline styles', regex: /\sstyle\s*=\s*["'][^"']+["']/gi },
{ name: 'javascript: URLs', regex: /href\s*=\s*["']javascript:[^"']*["']/gi }
];
cspPatterns.forEach(pattern => {
if (pattern.regex.test(newString)) {
conflicts.push({
instruction: inst.id,
issue: `New content contains ${pattern.name} (CSP violation)`,
severity: 'HIGH'
});
}
});
}
// Check pre-action-check requirement (inst_038)
if (inst.id === 'inst_038') {
// This will be checked in the hook itself
}
});
return {
passed: conflicts.length === 0,
conflicts,
relevantInstructions
};
}
/**
* Validate a Bash command
*/
validateBashCommand(command) {
const relevantInstructions = this.findRelevantInstructions('bash', command);
if (relevantInstructions.length === 0) {
return { passed: true, relevantInstructions: [] };
}
this.log('info', `Found ${relevantInstructions.length} relevant instructions for Bash command`);
const conflicts = [];
relevantInstructions.forEach(inst => {
// Check deployment instructions
if (inst.id === 'inst_025' && command.includes('rsync')) {
// Deployment directory structure check
// This is handled in validate-bash-command.js for detailed parsing
// Here we just flag it as relevant
this.log('info', `inst_025 (deployment structure) applies to this rsync command`);
}
// Check permission instructions
if (inst.id === 'inst_022' && command.includes('rsync') && !command.includes('--chmod')) {
if (command.includes('vps-93a693da') || command.includes('/var/www/')) {
conflicts.push({
instruction: inst.id,
issue: 'rsync to production without --chmod flag',
severity: 'MEDIUM'
});
}
}
});
return {
passed: conflicts.length === 0,
conflicts,
relevantInstructions
};
}
/**
* Find instructions relevant to a specific action
*/
findRelevantInstructions(actionType, context) {
if (!this.instructions) {
return [];
}
const relevant = [];
this.instructions.forEach(inst => {
// Only check active HIGH or MEDIUM persistence instructions
if (!inst.active) return;
if (inst.persistence !== 'HIGH' && inst.persistence !== 'MEDIUM') return;
// Match by action type and context
let isRelevant = false;
if (actionType === 'file-edit' || actionType === 'file-write') {
// CSP instructions
if (inst.id === 'inst_008') isRelevant = true;
// Pre-action-check requirement
if (inst.id === 'inst_038') isRelevant = true;
// File-specific instructions
if (inst.text && inst.text.toLowerCase().includes('file')) isRelevant = true;
}
if (actionType === 'bash') {
// Deployment instructions
if (inst.id === 'inst_025' && context.includes('rsync')) isRelevant = true;
if (inst.id === 'inst_020' && context.includes('rsync')) isRelevant = true;
if (inst.id === 'inst_022' && context.includes('rsync')) isRelevant = true;
// Git commit instructions
if (context.includes('git commit') && inst.text && inst.text.includes('git')) {
isRelevant = true;
}
}
if (isRelevant) {
relevant.push(inst);
}
});
return relevant;
}
/**
* Update session state with validation activity
*/
updateSessionState() {
try {
let sessionState = {};
if (fs.existsSync(SESSION_STATE_PATH)) {
sessionState = JSON.parse(fs.readFileSync(SESSION_STATE_PATH, 'utf8'));
}
if (!sessionState.framework_components) {
sessionState.framework_components = {};
}
if (!sessionState.framework_components.CrossReferenceValidator) {
sessionState.framework_components.CrossReferenceValidator = {
message: 0,
tokens: 0,
timestamp: null,
last_validation: null,
validations_performed: 0
};
}
sessionState.framework_components.CrossReferenceValidator.last_validation = new Date().toISOString();
sessionState.framework_components.CrossReferenceValidator.validations_performed += 1;
sessionState.framework_components.CrossReferenceValidator.timestamp = new Date().toISOString();
fs.writeFileSync(SESSION_STATE_PATH, JSON.stringify(sessionState, null, 2));
} catch (error) {
this.log('warning', `Could not update session state: ${error.message}`);
}
}
/**
* Main validation entry point
*/
validate(actionType, context) {
this.log('info', `Validating ${actionType} action`);
let result;
if (actionType === 'file-edit') {
result = this.validateFileEdit(context.filePath, context.oldString, context.newString);
} else if (actionType === 'file-write') {
result = this.validateFileEdit(context.filePath, '', context.content);
} else if (actionType === 'bash') {
result = this.validateBashCommand(context.command);
} else {
result = { passed: true, relevantInstructions: [] };
}
this.updateSessionState();
if (!result.passed) {
this.log('error', `Validation failed: ${result.conflicts.length} conflicts found`);
result.conflicts.forEach(c => {
this.log('error', ` ${c.instruction}: ${c.issue}`);
});
} else if (result.relevantInstructions.length > 0) {
this.log('success', `Validation passed (${result.relevantInstructions.length} relevant instructions checked)`);
} else {
this.log('success', 'Validation passed (no relevant instructions)');
}
return result;
}
}
// Export for use as module
if (typeof module !== 'undefined' && module.exports) {
module.exports = CrossReferenceValidator;
}
// CLI mode - allow direct invocation
if (require.main === module) {
const args = process.argv.slice(2);
const actionType = args[0];
const contextJSON = args[1];
if (!actionType || !contextJSON) {
console.error('Usage: node CrossReferenceValidator.js <action-type> <context-json>');
console.error('Example: node CrossReferenceValidator.js bash \'{"command":"rsync ..."}\'');
process.exit(1);
}
try {
const context = JSON.parse(contextJSON);
const validator = new CrossReferenceValidator();
const result = validator.validate(actionType, context);
if (result.passed) {
console.log('✓ Validation passed');
process.exit(0);
} else {
console.log('✗ Validation failed');
process.exit(1);
}
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(2);
}
}