tractatus/scripts/track-action-patterns.js
TheFlow 0e6be3eaf1 refactor: remove website code and fix critical startup crashes (Phase 8)
CRITICAL FIX: Server would CRASH ON STARTUP (multiple import errors)

REMOVED (2 scripts):
1. scripts/framework-watchdog.js
   - Monitored .claude/session-state.json (OUR Claude Code setup)
   - Monitored .claude/token-checkpoints.json (OUR file structure)
   - Implementers won't have our .claude/ directory

2. scripts/init-db.js
   - Created website collections: blog_posts, media_inquiries, case_submissions
   - Created website collections: resources, moderation_queue, users, citations
   - Created website collections: translations, koha_donations
   - Next steps referenced deleted scripts (npm run seed:admin)

REWRITTEN (2 files):

src/models/index.js (29 lines → 27 lines)
- REMOVED imports: Document, BlogPost, MediaInquiry, CaseSubmission, Resource
- REMOVED imports: ModerationQueue, User (all deleted in Phase 2)
- KEPT imports: AuditLog, DeliberationSession, GovernanceLog, GovernanceRule
- KEPT imports: Precedent, Project, SessionState, VariableValue, VerificationLog
- Result: Only framework models exported

src/server.js (284 lines → 163 lines, 43% reduction)
- REMOVED: Imports to deleted middleware (csrf-protection, response-sanitization)
- REMOVED: Stripe webhook handling (/api/koha/webhook)
- REMOVED: Static file caching (for deleted public/ directory)
- REMOVED: Static file serving (public/ deleted in Phase 6)
- REMOVED: CSRF token endpoint
- REMOVED: Website homepage with "auth, documents, blog, admin" references
- REMOVED: Instruction sync (scripts/sync-instructions-to-db.js reference)
- REMOVED: Hardcoded log path (${process.env.HOME}/var/log/tractatus/...)
- REMOVED: Website-specific security middleware
- KEPT: Security headers, rate limiting, CORS, body parsers
- KEPT: API routes, governance services, MongoDB connections
- RESULT: Clean framework-only server

RESULT: Repository can now start without crashes, all imports resolve

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 22:17:02 +13:00

237 lines
7 KiB
JavaScript
Executable file

#!/usr/bin/env node
/**
* Action Pattern Tracker
*
* Tracks patterns indicating "stuck in loop" situations:
* - Same file edited multiple times consecutively
* - Same function/section modified repeatedly
* - User frustration signals in messages
*
* This feeds into:
* - MetacognitiveVerifier (triggers verification on 3+ failed attempts)
* - ContextPressureMonitor (elevates pressure on loop detection)
*
* Usage:
* node scripts/track-action-patterns.js --action edit --file path/to/file
* node scripts/track-action-patterns.js --check
*/
const fs = require('fs');
const path = require('path');
const SESSION_STATE_PATH = path.join(__dirname, '../.claude/session-state.json');
const ACTION_PATTERNS_PATH = path.join(__dirname, '../.claude/action-patterns.json');
/**
* Load session state
*/
function loadSessionState() {
try {
return JSON.parse(fs.readFileSync(SESSION_STATE_PATH, 'utf8'));
} catch (err) {
console.error(`Error loading session state: ${err.message}`);
process.exit(1);
}
}
/**
* Load action patterns (or initialize)
*/
function loadActionPatterns() {
try {
if (fs.existsSync(ACTION_PATTERNS_PATH)) {
return JSON.parse(fs.readFileSync(ACTION_PATTERNS_PATH, 'utf8'));
}
} catch (err) {
console.warn(`Warning: Could not load action patterns: ${err.message}`);
}
// Initialize new patterns structure
return {
version: "1.0",
session_id: null,
started: new Date().toISOString(),
actions: [],
patterns: {
repeated_edits: [],
same_file_edits: {},
user_frustration_signals: []
},
alerts: [],
last_updated: new Date().toISOString()
};
}
/**
* Save action patterns
*/
function saveActionPatterns(patterns) {
patterns.last_updated = new Date().toISOString();
fs.writeFileSync(ACTION_PATTERNS_PATH, JSON.stringify(patterns, null, 2));
}
/**
* Track new action
*/
function trackAction(actionType, filePath) {
const patterns = loadActionPatterns();
const sessionState = loadSessionState();
// Update session ID if changed
if (patterns.session_id !== sessionState.session_id) {
patterns.session_id = sessionState.session_id;
patterns.actions = []; // Reset for new session
patterns.patterns.same_file_edits = {};
patterns.patterns.repeated_edits = [];
patterns.alerts = [];
}
// Add action
const action = {
type: actionType,
file: filePath,
timestamp: new Date().toISOString(),
message_number: sessionState.message_count
};
patterns.actions.push(action);
// Track same-file edits
if (actionType === 'edit' || actionType === 'write') {
if (!patterns.patterns.same_file_edits[filePath]) {
patterns.patterns.same_file_edits[filePath] = {
count: 0,
timestamps: [],
alert_threshold: 3
};
}
patterns.patterns.same_file_edits[filePath].count++;
patterns.patterns.same_file_edits[filePath].timestamps.push(action.timestamp);
// Check for loop pattern
if (patterns.patterns.same_file_edits[filePath].count >= 3) {
// Check if edits are recent (within last 10 actions)
const recentActions = patterns.actions.slice(-10);
const recentEditsOfThisFile = recentActions.filter(a =>
(a.type === 'edit' || a.type === 'write') && a.file === filePath
);
if (recentEditsOfThisFile.length >= 3) {
const alert = {
type: 'repeated_file_edit',
severity: 'MEDIUM',
file: filePath,
count: patterns.patterns.same_file_edits[filePath].count,
message: `File edited ${patterns.patterns.same_file_edits[filePath].count} times - possible stuck loop`,
timestamp: new Date().toISOString(),
recommendation: 'Run MetacognitiveVerifier to assess current approach'
};
patterns.alerts.push(alert);
console.log(`⚠️ ALERT: Repeated edits to ${path.basename(filePath)}`);
console.log(` Count: ${patterns.patterns.same_file_edits[filePath].count}`);
console.log(` Recommendation: Pause and verify approach`);
}
}
}
// Keep only last 100 actions
if (patterns.actions.length > 100) {
patterns.actions = patterns.actions.slice(-100);
}
saveActionPatterns(patterns);
return patterns;
}
/**
* Check for active patterns/alerts
*/
function checkPatterns() {
const patterns = loadActionPatterns();
const activeAlerts = patterns.alerts.filter(alert => {
// Consider alerts from last hour as active
const alertTime = new Date(alert.timestamp);
const hourAgo = new Date(Date.now() - 60 * 60 * 1000);
return alertTime > hourAgo;
});
if (activeAlerts.length === 0) {
console.log('✅ No active pattern alerts');
return { hasAlerts: false, alerts: [] };
}
console.log(`⚠️ ${activeAlerts.length} active alert(s):`);
activeAlerts.forEach((alert, i) => {
console.log(`\n${i + 1}. [${alert.severity}] ${alert.type}`);
console.log(` ${alert.message}`);
console.log(` Recommendation: ${alert.recommendation}`);
});
return { hasAlerts: true, alerts: activeAlerts };
}
/**
* Get pattern summary
*/
function getPatternSummary() {
const patterns = loadActionPatterns();
const summary = {
total_actions: patterns.actions.length,
files_edited_multiple_times: Object.keys(patterns.patterns.same_file_edits).filter(
file => patterns.patterns.same_file_edits[file].count >= 2
),
active_alerts: patterns.alerts.filter(alert => {
const alertTime = new Date(alert.timestamp);
const hourAgo = new Date(Date.now() - 60 * 60 * 1000);
return alertTime > hourAgo;
}).length
};
return summary;
}
/**
* Main
*/
function main() {
const args = process.argv.slice(2);
if (args.includes('--check')) {
checkPatterns();
} else if (args.includes('--summary')) {
const summary = getPatternSummary();
console.log('Action Pattern Summary:');
console.log(` Total actions: ${summary.total_actions}`);
console.log(` Files with multiple edits: ${summary.files_edited_multiple_times.length}`);
if (summary.files_edited_multiple_times.length > 0) {
summary.files_edited_multiple_times.forEach(file => {
console.log(` - ${path.basename(file)}`);
});
}
console.log(` Active alerts: ${summary.active_alerts}`);
} else if (args.includes('--action')) {
const actionIndex = args.indexOf('--action');
const fileIndex = args.indexOf('--file');
if (actionIndex === -1 || fileIndex === -1) {
console.error('Usage: --action <type> --file <path>');
process.exit(1);
}
const actionType = args[actionIndex + 1];
const filePath = args[fileIndex + 1];
trackAction(actionType, filePath);
console.log(`✅ Tracked: ${actionType} on ${path.basename(filePath)}`);
} else {
console.log('Usage:');
console.log(' --action <type> --file <path> Track an action');
console.log(' --check Check for active alerts');
console.log(' --summary Show pattern summary');
}
}
main();