tractatus/scripts/validate-deployment.js
TheFlow 9bc2410420 feat(framework): implement 6 high-priority governance enhancements
SUMMARY:
Implemented 6 framework refinements identified from incident analysis
(inst_049 and inst_025 violations). These enhancements provide architectural
enforcement for patterns that previously relied on voluntary compliance.

ENHANCEMENTS IMPLEMENTED:

1. Instruction Analytics Script (Priority 8)
   - scripts/analyze-instruction-violations.js
   - Analyzes instruction-history.json for usage patterns
   - Identifies most violated instructions
   - Calculates enforcement effectiveness (hook vs. voluntary)
   - Shows 97.2% voluntary compliance, 75% hook enforcement
   - Recommendations for converting voluntary → architectural

2. Framework Incidents Database (Priority 7)
   - .claude/framework-incidents.json
   - Structured tracking of framework violations
   - INC-001: Ignored user hypothesis (70k tokens wasted)
   - INC-002: Deployment directory flattening (inst_025 violation)
   - Statistics: 2 incidents, 75k tokens wasted, 4.5 hours lost

3. Loop Detector Module (Priorities 3 & 4)
   - scripts/framework-components/LoopDetector.js
   - Detects "stuck in loop" patterns
   - Triggers: 3+ edits to same file, repeated action types
   - Feeds into MetacognitiveVerifier and ContextPressureMonitor
   - Calculates pressure contribution (5-40 points by severity)

4. Action Pattern Tracker (Priority 3 & 4)
   - scripts/track-action-patterns.js
   - Tracks edit/write actions to detect repetition
   - Alerts after 3 consecutive edits to same file
   - Maintains action history (last 100 actions)
   - Recommendations for metacognitive verification

5. Pre-Deployment Validation (Priority 5)
   - scripts/validate-deployment.js
   - Validates rsync/scp commands against inst_025
   - Detects directory structure flattening
   - Suggests separate commands for different directories
   - Prevents 4th documented occurrence of deployment errors

6. User Suggestion Tracker (Priority 6)
   - scripts/track-user-suggestions.js
   - Implements inst_049: "Test user hypothesis first"
   - Tracks user technical hypotheses
   - Flags untested hypotheses as HIGH priority
   - Integrates with MetacognitiveVerifier for compliance

USAGE:

Instruction Analytics:
  node scripts/analyze-instruction-violations.js

Loop Detection:
  node scripts/track-action-patterns.js --check
  node scripts/track-action-patterns.js --summary

Deployment Validation:
  node scripts/validate-deployment.js --command "rsync ..."

User Suggestions:
  node scripts/track-user-suggestions.js --add "hypothesis text"
  node scripts/track-user-suggestions.js --check-untested

IMPACT:
- Converts 6 voluntary compliance patterns to architectural enforcement
- Prevents repeat of documented 75k token waste
- Provides visibility into framework effectiveness
- Establishes foundation for future hook integration

METRICS FROM ANALYTICS:
- Active Instructions: 40
- Voluntary Compliance: 97.2%
- Hook Enforcement: 75.0%
- Recorded Violations: 2
- Tokens Wasted: 75,000

NEXT STEPS:
- Integrate LoopDetector into MetacognitiveVerifier.service.js
- Add Pre-Deployment Validation to Bash command validator hook
- Wire User Suggestion Tracker into BoundaryEnforcer checks
- Document successful compliance patterns (7 STRATEGIC instructions at 100%)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 20:41:10 +13:00

236 lines
5.9 KiB
JavaScript
Executable file

#!/usr/bin/env node
/**
* Pre-Deployment Validation
*
* Validates rsync/scp/deployment commands against inst_025 rules:
* - Checks if source files have different subdirectories
* - Ensures separate commands for different directory levels
* - Prevents directory structure flattening
*
* Usage:
* node scripts/validate-deployment.js --command "rsync ..."
* node scripts/validate-deployment.js --files "file1 file2" --target "remote:path"
*
* Exit codes:
* 0 = Valid deployment
* 1 = Invalid (violates inst_025)
* 2 = Error
*
* Copyright 2025 Tractatus Project
* Licensed under Apache License 2.0
*/
const fs = require('fs');
const path = require('path');
/**
* Parse rsync command to extract source files and target
*/
function parseRsyncCommand(command) {
// Match rsync with options and files
const rsyncPattern = /rsync\s+([^"'\s]+(?:\s+[^"'\s]+)*)\s+((?:[^\s]+\s+)*)([\w@.-]+:[^\s]+|[^\s]+)$/;
const match = command.match(rsyncPattern);
if (!match) {
return null;
}
// Extract flags and files
const parts = command.split(/\s+/).filter(p => p.length > 0);
const rsyncIndex = parts.findIndex(p => p === 'rsync' || p.endsWith('/rsync'));
if (rsyncIndex === -1) {
return null;
}
const filesAndTarget = parts.slice(rsyncIndex + 1);
// Last item is target
const target = filesAndTarget[filesAndTarget.length - 1];
// Everything before target that's not a flag is a file
const files = [];
for (let i = 0; i < filesAndTarget.length - 1; i++) {
const item = filesAndTarget[i];
if (!item.startsWith('-')) {
files.push(item);
}
}
return {
files,
target,
command
};
}
/**
* Check if files have different subdirectory paths
*/
function checkDirectoryMismatch(files) {
if (files.length <= 1) {
return { hasMismatch: false, directories: [] };
}
const directories = files.map(f => {
const dir = path.dirname(f);
return dir === '.' ? '' : dir;
});
const uniqueDirs = [...new Set(directories)];
return {
hasMismatch: uniqueDirs.length > 1,
directories: uniqueDirs,
filesByDir: Object.fromEntries(
uniqueDirs.map(dir => [
dir,
files.filter(f => path.dirname(f) === (dir || '.'))
])
)
};
}
/**
* Validate deployment command
*/
function validateDeployment(command) {
const parsed = parseRsyncCommand(command);
if (!parsed) {
return {
valid: false,
error: 'Could not parse rsync command',
suggestion: null
};
}
const { files, target } = parsed;
if (files.length === 0) {
return {
valid: false,
error: 'No source files specified',
suggestion: null
};
}
// Check for directory mismatch
const dirCheck = checkDirectoryMismatch(files);
if (!dirCheck.hasMismatch) {
return {
valid: true,
message: 'Deployment command is valid - all files in same directory',
files,
target
};
}
// Violation detected
return {
valid: false,
error: `inst_025 violation: Files from different subdirectories in single rsync command`,
details: {
file_count: files.length,
unique_directories: dirCheck.directories.length,
directories: dirCheck.directories,
filesByDir: dirCheck.filesByDir
},
suggestion: generateSeparateCommands(dirCheck.filesByDir, target, command)
};
}
/**
* Generate separate rsync commands for each directory
*/
function generateSeparateCommands(filesByDir, target, originalCommand) {
const commands = [];
// Extract rsync flags from original command
const flagMatch = originalCommand.match(/rsync\s+([^/\s][^\s]*)/);
const flags = flagMatch ? flagMatch[1] : '-avz --progress';
Object.entries(filesByDir).forEach(([dir, files]) => {
const targetWithDir = dir ? `${target}/${dir}/` : target;
files.forEach(file => {
const cmd = `rsync ${flags} ${file} ${targetWithDir}`;
commands.push(cmd);
});
});
return commands;
}
/**
* Display validation results
*/
function displayResults(result) {
if (result.valid) {
console.log('\x1b[32m✅ Deployment command is VALID\x1b[0m');
console.log(` Files: ${result.files.length}`);
console.log(` Target: ${result.target}`);
return 0;
}
console.log('\x1b[31m❌ Deployment command VIOLATES inst_025\x1b[0m');
console.log(`\n Error: ${result.error}\n`);
if (result.details) {
console.log(' Details:');
console.log(` File count: ${result.details.file_count}`);
console.log(` Unique directories: ${result.details.unique_directories}`);
console.log(' Directories:');
result.details.directories.forEach(dir => {
const dirDisplay = dir || '(root)';
const fileCount = result.details.filesByDir[dir].length;
console.log(`${dirDisplay} (${fileCount} files)`);
result.details.filesByDir[dir].forEach(file => {
console.log(` - ${path.basename(file)}`);
});
});
}
if (result.suggestion) {
console.log('\n \x1b[33mSuggested fix (separate commands per directory):\x1b[0m\n');
result.suggestion.forEach((cmd, i) => {
console.log(` ${i + 1}. ${cmd}`);
});
console.log('');
}
return 1;
}
/**
* Main
*/
function main() {
const args = process.argv.slice(2);
if (args.length === 0 || args.includes('--help')) {
console.log('Pre-Deployment Validation');
console.log('\nUsage:');
console.log(' node scripts/validate-deployment.js --command "rsync ..."');
console.log('\nExample:');
console.log(' node scripts/validate-deployment.js --command "rsync -avz file1 file2/sub/file remote:path"');
process.exit(0);
}
const commandIndex = args.indexOf('--command');
if (commandIndex === -1 || !args[commandIndex + 1]) {
console.error('Error: --command flag required');
process.exit(2);
}
const command = args[commandIndex + 1];
const result = validateDeployment(command);
const exitCode = displayResults(result);
process.exit(exitCode);
}
main();