tractatus/scripts/framework-health-metrics.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

332 lines
10 KiB
JavaScript
Executable file

#!/usr/bin/env node
/**
* Framework Health Metrics Dashboard
*
* Automated health report for Tractatus framework showing:
* - Component status and activity
* - Instruction database health
* - Token checkpoint status
* - Hook validator metrics
* - Recent incident trends
* - Overall health score
*
* Copyright 2025 Tractatus Project
* Licensed under Apache License 2.0
*/
const fs = require('fs');
const path = require('path');
// Paths
const SESSION_STATE_PATH = path.join(__dirname, '../.claude/session-state.json');
const INSTRUCTION_HISTORY_PATH = path.join(__dirname, '../.claude/instruction-history.json');
const TOKEN_CHECKPOINTS_PATH = path.join(__dirname, '../.claude/token-checkpoints.json');
const METRICS_PATH = path.join(__dirname, '../.claude/metrics/hooks-metrics.json');
const INCIDENTS_DIR = path.join(__dirname, '../docs/framework-incidents');
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
red: '\x1b[31m',
cyan: '\x1b[36m'
};
function log(message, color = 'reset') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
function header(message) {
console.log('');
log('═'.repeat(70), 'cyan');
log(` ${message}`, 'bright');
log('═'.repeat(70), 'cyan');
console.log('');
}
function section(message) {
console.log('');
log(`${message}`, 'blue');
}
function success(message) {
log(`${message}`, 'green');
}
function warning(message) {
log(`${message}`, 'yellow');
}
function error(message) {
log(`${message}`, 'red');
}
function loadJSON(filepath) {
try {
if (!fs.existsSync(filepath)) {
return null;
}
return JSON.parse(fs.readFileSync(filepath, 'utf8'));
} catch (err) {
return null;
}
}
function calculateHealthScore(metrics) {
let score = 100;
const issues = [];
// Component activity (20 points)
const activeComponents = metrics.components.filter(c => c.validations > 0).length;
const componentScore = (activeComponents / metrics.components.length) * 20;
score = Math.min(score, score - (20 - componentScore));
if (componentScore < 10) {
issues.push(`Low component activity: ${activeComponents}/${metrics.components.length} components used`);
}
// Instruction database health (20 points)
if (metrics.instructions.active > 60) {
score -= 10;
issues.push(`Too many active instructions: ${metrics.instructions.active} (target: <50)`);
}
if (metrics.instructions.highPersistence < 80) {
score -= 10;
issues.push(`Low HIGH persistence ratio: ${metrics.instructions.highPersistence.toFixed(1)}%`);
}
// Token checkpoint compliance (15 points)
const missedCheckpoints = metrics.checkpoints.total - metrics.checkpoints.completed;
if (metrics.checkpoints.overdue) {
score -= 15;
issues.push(`Token checkpoints overdue: ${missedCheckpoints} missed`);
}
// Hook validator effectiveness (15 points)
if (metrics.hooks.totalExecutions === 0) {
score -= 15;
issues.push('No hook validations recorded');
} else {
const blockRate = metrics.hooks.totalBlocks / metrics.hooks.totalExecutions;
if (blockRate > 0.2) {
score -= 5;
issues.push(`High hook block rate: ${(blockRate * 100).toFixed(1)}% (may indicate issues)`);
}
}
// Recent incidents (30 points)
const recentIncidents = metrics.incidents.recent30Days;
if (recentIncidents >= 5) {
score -= 20;
issues.push(`High incident rate: ${recentIncidents} in last 30 days`);
} else if (recentIncidents >= 3) {
score -= 10;
issues.push(`Moderate incident rate: ${recentIncidents} in last 30 days`);
}
return {
score: Math.max(0, score),
grade: score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : score >= 60 ? 'D' : 'F',
issues
};
}
function main() {
header('Framework Health Metrics Dashboard');
const metrics = {
components: [],
instructions: {},
checkpoints: {},
hooks: {},
incidents: {}
};
// 1. Component Status
section('1. Framework Component Activity');
const sessionState = loadJSON(SESSION_STATE_PATH);
if (sessionState && sessionState.framework_components) {
const components = [
'ContextPressureMonitor',
'InstructionPersistenceClassifier',
'CrossReferenceValidator',
'BoundaryEnforcer',
'MetacognitiveVerifier',
'PluralisticDeliberationOrchestrator',
'BashCommandValidator'
];
components.forEach(name => {
const component = sessionState.framework_components[name];
if (component) {
const validations = component.validations_performed || 0;
const status = validations > 0 ? 'ACTIVE' : 'READY';
const color = validations > 0 ? 'green' : 'yellow';
log(` ${name}:`, 'cyan');
log(` Status: ${status} (${validations} validations)`, color);
metrics.components.push({ name, status, validations });
}
});
} else {
warning('Session state not found - run: node scripts/session-init.js');
}
// 2. Instruction Database Health
section('2. Instruction Database Health');
const instructionHistory = loadJSON(INSTRUCTION_HISTORY_PATH);
if (instructionHistory) {
const active = instructionHistory.instructions.filter(i => i.active);
const inactive = instructionHistory.instructions.filter(i => !i.active);
const highPersistence = active.filter(i => i.persistence === 'HIGH');
log(` Total instructions: ${instructionHistory.instructions.length}`, 'cyan');
log(` Active: ${active.length}`, active.length < 50 ? 'green' : 'yellow');
log(` Inactive: ${inactive.length}`, 'cyan');
log(` HIGH persistence: ${highPersistence.length} (${(highPersistence.length/active.length*100).toFixed(1)}%)`, 'cyan');
log(` Database version: ${instructionHistory.version}`, 'cyan');
if (active.length < 50) {
success('✓ Instruction count optimal (<50)');
} else {
warning(`⚠ Instruction count high: ${active.length} (target: <50)`);
}
metrics.instructions = {
total: instructionHistory.instructions.length,
active: active.length,
inactive: inactive.length,
highPersistence: (highPersistence.length / active.length) * 100
};
}
// 3. Token Checkpoint Status
section('3. Token Checkpoint Status');
const checkpoints = loadJSON(TOKEN_CHECKPOINTS_PATH);
if (checkpoints) {
const completed = checkpoints.checkpoints.filter(c => c.completed).length;
const total = checkpoints.checkpoints.length;
log(` Budget: ${checkpoints.budget.toLocaleString()} tokens`, 'cyan');
log(` Checkpoints completed: ${completed}/${total}`, completed === total ? 'green' : 'yellow');
log(` Next checkpoint: ${checkpoints.next_checkpoint ? checkpoints.next_checkpoint.toLocaleString() + ' tokens' : 'All complete'}`, 'cyan');
if (checkpoints.overdue) {
warning('⚠ Checkpoint overdue - run: node scripts/check-token-checkpoint.js');
} else {
success('✓ Checkpoint monitoring current');
}
metrics.checkpoints = {
total,
completed,
overdue: checkpoints.overdue
};
}
// 4. Hook Validator Metrics
section('4. Hook Validator Metrics');
const hooksMetrics = loadJSON(METRICS_PATH);
if (hooksMetrics && hooksMetrics.session_stats) {
const stats = hooksMetrics.session_stats;
log(` Total hook executions: ${stats.total_bash_hooks || 0}`, 'cyan');
log(` Blocks issued: ${stats.total_bash_blocks || 0}`, 'cyan');
const blockRate = stats.total_bash_hooks > 0
? ((stats.total_bash_blocks / stats.total_bash_hooks) * 100).toFixed(1)
: 0;
log(` Block rate: ${blockRate}%`, parseFloat(blockRate) < 10 ? 'green' : 'yellow');
success('✓ Hook validators operational');
metrics.hooks = {
totalExecutions: stats.total_bash_hooks || 0,
totalBlocks: stats.total_bash_blocks || 0
};
} else {
log(` No hook metrics available yet`, 'yellow');
metrics.hooks = { totalExecutions: 0, totalBlocks: 0 };
}
// 5. Recent Incidents
section('5. Recent Framework Incidents');
if (fs.existsSync(INCIDENTS_DIR)) {
const incidents = fs.readdirSync(INCIDENTS_DIR)
.filter(f => f.endsWith('.md'))
.map(f => {
const filepath = path.join(INCIDENTS_DIR, f);
const stats = fs.statSync(filepath);
return { file: f, date: stats.mtime };
})
.sort((a, b) => b.date - a.date);
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
const recent = incidents.filter(i => i.date > thirtyDaysAgo);
log(` Total incidents: ${incidents.length}`, 'cyan');
log(` Last 30 days: ${recent.length}`, recent.length === 0 ? 'green' : recent.length < 3 ? 'yellow' : 'red');
if (recent.length > 0) {
console.log('');
log(` Most recent:`, 'bold');
recent.slice(0, 3).forEach(i => {
const daysAgo = Math.floor((Date.now() - i.date.getTime()) / (1000 * 60 * 60 * 24));
log(` - ${i.file} (${daysAgo} days ago)`, 'cyan');
});
} else {
success('✓ No incidents in last 30 days');
}
metrics.incidents = {
total: incidents.length,
recent30Days: recent.length
};
} else {
metrics.incidents = { total: 0, recent30Days: 0 };
}
// 6. Overall Health Score
section('6. Overall Framework Health');
const health = calculateHealthScore(metrics);
console.log('');
const scoreColor = health.score >= 90 ? 'green' : health.score >= 70 ? 'yellow' : 'red';
log(` Health Score: ${health.score}/100 (Grade: ${health.grade})`, scoreColor);
if (health.issues.length === 0) {
console.log('');
success('✓ No issues detected - framework operating optimally');
} else {
console.log('');
log(` Issues Identified (${health.issues.length}):`, 'yellow');
health.issues.forEach(issue => {
warning(issue);
});
}
console.log('');
log('═'.repeat(70), 'cyan');
if (health.score >= 90) {
success('Framework Status: EXCELLENT');
} else if (health.score >= 70) {
warning('Framework Status: GOOD (minor improvements recommended)');
} else {
error('Framework Status: NEEDS ATTENTION');
}
log('═'.repeat(70), 'cyan');
console.log('');
}
main();