tractatus/scripts/analyze-instruction-database.js
TheFlow 2298d36bed 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

262 lines
8.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Instruction Database Analysis
*
* Analyzes instruction-history.json for optimization opportunities:
* - Identifies obsolete instructions
* - Finds redundancies and consolidation candidates
* - Suggests archival opportunities
*/
const fs = require('fs');
const path = require('path');
const INSTRUCTION_HISTORY_PATH = path.join(__dirname, '../.claude/instruction-history.json');
const colors = {
reset: '\x1b[0m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
red: '\x1b[31m',
cyan: '\x1b[36m',
bold: '\x1b[1m'
};
function log(message, color = 'reset') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
function header(message) {
console.log('');
log('═'.repeat(70), 'cyan');
log(` ${message}`, 'bold');
log('═'.repeat(70), 'cyan');
console.log('');
}
function section(message) {
console.log('');
log(`${message}`, 'blue');
}
// Load instruction history
const history = JSON.parse(fs.readFileSync(INSTRUCTION_HISTORY_PATH, 'utf8'));
const active = history.instructions.filter(i => i.active);
const inactive = history.instructions.filter(i => !i.active);
header('Instruction Database Analysis');
// 1. Summary Statistics
section('1. Database Statistics');
log(` Total instructions: ${history.instructions.length}`, 'cyan');
log(` Active instructions: ${active.length}`, 'cyan');
log(` Inactive instructions: ${inactive.length}`, 'cyan');
console.log('');
// By persistence
const byPersistence = {
HIGH: active.filter(i => i.persistence === 'HIGH').length,
MEDIUM: active.filter(i => i.persistence === 'MEDIUM').length,
LOW: active.filter(i => i.persistence === 'LOW').length
};
log(` By persistence:`, 'bold');
log(` HIGH: ${byPersistence.HIGH} (${(byPersistence.HIGH/active.length*100).toFixed(1)}%)`, 'green');
log(` MEDIUM: ${byPersistence.MEDIUM} (${(byPersistence.MEDIUM/active.length*100).toFixed(1)}%)`, 'yellow');
log(` LOW: ${byPersistence.LOW} (${(byPersistence.LOW/active.length*100).toFixed(1)}%)`, 'yellow');
console.log('');
// By quadrant
const byQuadrant = {
STRATEGIC: active.filter(i => i.quadrant === 'STRATEGIC').length,
SYSTEM: active.filter(i => i.quadrant === 'SYSTEM').length,
OPERATIONAL: active.filter(i => i.quadrant === 'OPERATIONAL').length,
TACTICAL: active.filter(i => i.quadrant === 'TACTICAL').length
};
log(` By quadrant:`, 'bold');
log(` STRATEGIC: ${byQuadrant.STRATEGIC}`, 'cyan');
log(` SYSTEM: ${byQuadrant.SYSTEM}`, 'cyan');
log(` OPERATIONAL: ${byQuadrant.OPERATIONAL}`, 'cyan');
log(` TACTICAL: ${byQuadrant.TACTICAL}`, 'cyan');
console.log('');
// By temporal scope
const byScope = {
PERMANENT: active.filter(i => i.temporal_scope === 'PERMANENT').length,
PROJECT: active.filter(i => i.temporal_scope === 'PROJECT').length,
SESSION: active.filter(i => i.temporal_scope === 'SESSION').length
};
log(` By temporal scope:`, 'bold');
log(` PERMANENT: ${byScope.PERMANENT}`, 'green');
log(` PROJECT: ${byScope.PROJECT}`, 'yellow');
log(` SESSION: ${byScope.SESSION || 0}`, 'red');
// 2. Consolidation Candidates
section('2. Instruction Series (Consolidation Candidates)');
// Find instruction series (e.g., inst_024a, inst_024b, etc.)
const series = {};
active.forEach(inst => {
const match = inst.id.match(/^(inst_\d+)[a-z]$/);
if (match) {
const base = match[1];
if (!series[base]) series[base] = [];
series[base].push(inst);
}
});
if (Object.keys(series).length > 0) {
for (const [base, instructions] of Object.entries(series)) {
log(` ${base} series: ${instructions.length} instructions`, 'yellow');
instructions.forEach(inst => {
const wordCount = inst.text.split(' ').length;
log(` - ${inst.id}: ${wordCount} words - ${inst.text.substring(0, 60)}...`, 'cyan');
});
console.log('');
}
log(` Recommendation: Consider consolidating instruction series into single comprehensive instructions`, 'yellow');
} else {
log(` No instruction series found`, 'green');
}
// 3. PROJECT-scoped instructions
section('3. PROJECT-Scoped Instructions (May Be Obsolete)');
const projectScoped = active.filter(i => i.temporal_scope === 'PROJECT');
log(` Found ${projectScoped.length} PROJECT-scoped instructions`, 'cyan');
console.log('');
projectScoped.forEach(inst => {
const age = inst.created_date || 'unknown';
log(` ${inst.id} (${age}): ${inst.text.substring(0, 70)}...`, 'yellow');
});
if (projectScoped.length > 0) {
console.log('');
log(` Recommendation: Review PROJECT-scoped instructions - convert to PERMANENT or archive if no longer needed`, 'yellow');
}
// 4. TACTICAL/MEDIUM/LOW persistence
section('4. Lower Persistence Instructions (Archival Candidates)');
const lowerPersistence = active.filter(i => i.persistence !== 'HIGH');
log(` Found ${lowerPersistence.length} non-HIGH persistence instructions`, 'cyan');
console.log('');
lowerPersistence.forEach(inst => {
log(` ${inst.id} (${inst.persistence}): ${inst.text.substring(0, 70)}...`, 'yellow');
});
if (lowerPersistence.length > 0) {
console.log('');
log(` Recommendation: Review MEDIUM/LOW persistence - promote to HIGH or archive`, 'yellow');
}
// 5. Keyword-based redundancy detection
section('5. Potential Redundancies (Keyword Analysis)');
// Group by common keywords
const keywords = {};
active.forEach(inst => {
const words = inst.text.toLowerCase()
.replace(/[^a-z\s]/g, '')
.split(/\s+/)
.filter(w => w.length > 5); // Only words longer than 5 chars
words.forEach(word => {
if (!keywords[word]) keywords[word] = [];
keywords[word].push(inst.id);
});
});
// Find keywords appearing in multiple instructions (potential redundancy)
const commonKeywords = Object.entries(keywords)
.filter(([word, ids]) => ids.length >= 3)
.sort((a, b) => b[1].length - a[1].length)
.slice(0, 10);
if (commonKeywords.length > 0) {
log(` Top keywords appearing in multiple instructions:`, 'cyan');
console.log('');
commonKeywords.forEach(([word, ids]) => {
log(` "${word}": ${ids.length} instructions (${ids.slice(0, 5).join(', ')})`, 'yellow');
});
console.log('');
log(` Recommendation: Review instructions with shared keywords for potential consolidation`, 'yellow');
}
// 6. Long instructions (>100 words)
section('6. Very Long Instructions (Simplification Candidates)');
const longInstructions = active.filter(i => i.text.split(' ').length > 100);
log(` Found ${longInstructions.length} instructions > 100 words`, 'cyan');
console.log('');
longInstructions.forEach(inst => {
const wordCount = inst.text.split(' ').length;
log(` ${inst.id} (${wordCount} words): ${inst.text.substring(0, 70)}...`, 'yellow');
});
if (longInstructions.length > 0) {
console.log('');
log(` Recommendation: Consider splitting long instructions or consolidating into procedures`, 'yellow');
}
// 7. Optimization Summary
section('7. Optimization Recommendations');
const recommendations = [];
if (Object.keys(series).length > 0) {
const seriesCount = Object.values(series).reduce((sum, arr) => sum + arr.length, 0);
const consolidatedCount = Object.keys(series).length;
const savings = seriesCount - consolidatedCount;
recommendations.push({
action: `Consolidate instruction series`,
savings,
details: `${Object.keys(series).length} series with ${seriesCount} total instructions`
});
}
if (projectScoped.length > 5) {
recommendations.push({
action: `Review PROJECT-scoped instructions`,
savings: Math.floor(projectScoped.length * 0.3),
details: `${projectScoped.length} PROJECT-scoped (estimate 30% can be archived)`
});
}
if (lowerPersistence.length > 0) {
recommendations.push({
action: `Archive/promote MEDIUM/LOW persistence`,
savings: Math.floor(lowerPersistence.length * 0.5),
details: `${lowerPersistence.length} non-HIGH persistence (estimate 50% can be archived)`
});
}
if (recommendations.length === 0) {
log(` No obvious optimization opportunities found`, 'green');
log(` Database appears well-optimized at ${active.length} active instructions`, 'green');
} else {
let totalSavings = 0;
recommendations.forEach((rec, idx) => {
log(` ${idx + 1}. ${rec.action}`, 'yellow');
log(` Potential savings: ${rec.savings} instructions`, 'cyan');
log(` Details: ${rec.details}`, 'cyan');
console.log('');
totalSavings += rec.savings;
});
const target = active.length - totalSavings;
log(` Estimated result: ${active.length}${target} active instructions (${totalSavings} reduction)`, 'bold');
if (target < 50) {
log(` ✓ Target <50 achievable!`, 'green');
} else {
log(` ⚠ May need additional optimization to reach <50`, 'yellow');
}
}
console.log('');
log('═'.repeat(70), 'cyan');