#!/usr/bin/env node /** * Tractatus Session Initialization * * Automatically runs all mandatory framework checks at session start. * Should be called at the beginning of every Claude Code session. * * Copyright 2025 Tractatus Project * Licensed under Apache License 2.0 */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); 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'); /** * Color output helpers */ 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'); } /** * Check if this is a new session or restart */ function isNewSession() { try { const sessionState = JSON.parse(fs.readFileSync(SESSION_STATE_PATH, 'utf8')); // Check if session_id is today's date const today = new Date().toISOString().split('T')[0]; const sessionDate = sessionState.session_id.split('-').slice(0, 3).join('-'); // Check if message count is 0 (new session) const isNew = sessionState.message_count === 0; // Check if session started today const isToday = sessionDate === today; return { isNew, isToday, sessionState }; } catch (err) { // If file doesn't exist or can't be read, treat as new session return { isNew: true, isToday: true, sessionState: null }; } } /** * Initialize session state */ function initializeSessionState() { const sessionId = new Date().toISOString().split('T')[0] + '-001'; const timestamp = new Date().toISOString(); const sessionState = { version: '1.0.0', session_id: sessionId, started: timestamp, message_count: 1, token_estimate: 0, last_framework_activity: { ContextPressureMonitor: { message: 1, tokens: 0, timestamp: timestamp, last_level: 'NORMAL', last_score: 0 }, InstructionPersistenceClassifier: { message: 0, tokens: 0, timestamp: null, last_classification: null }, CrossReferenceValidator: { message: 0, tokens: 0, timestamp: null, last_validation: null }, BoundaryEnforcer: { message: 0, tokens: 0, timestamp: null, last_check: null }, MetacognitiveVerifier: { message: 0, tokens: 0, timestamp: null, last_verification: null }, PluralisticDeliberationOrchestrator: { message: 0, tokens: 0, timestamp: null, last_deliberation: null } }, staleness_thresholds: { messages: 20, tokens: 30000 }, alerts: [], last_updated: timestamp, initialized: true }; fs.writeFileSync(SESSION_STATE_PATH, JSON.stringify(sessionState, null, 2)); return sessionState; } /** * Reset token checkpoints for new session */ function resetTokenCheckpoints() { const checkpoints = { version: '1.0.0', budget: 200000, checkpoints: [ { percentage: 25, tokens: 50000, completed: false, timestamp: null }, { percentage: 50, tokens: 100000, completed: false, timestamp: null }, { percentage: 75, tokens: 150000, completed: false, timestamp: null } ], next_checkpoint: 50000, overdue: false, last_check: new Date().toISOString() }; fs.writeFileSync(TOKEN_CHECKPOINTS_PATH, JSON.stringify(checkpoints, null, 2)); return checkpoints; } /** * Load and summarize instruction history */ function loadInstructionHistory() { try { if (!fs.existsSync(INSTRUCTION_HISTORY_PATH)) { return { total: 0, high: 0, medium: 0, low: 0 }; } const history = JSON.parse(fs.readFileSync(INSTRUCTION_HISTORY_PATH, 'utf8')); const active = history.instructions?.filter(i => i.active) || []; const summary = { total: active.length, high: active.filter(i => i.persistence === 'HIGH').length, medium: active.filter(i => i.persistence === 'MEDIUM').length, low: active.filter(i => i.persistence === 'LOW').length, strategic: active.filter(i => i.quadrant === 'STRATEGIC').length, system: active.filter(i => i.quadrant === 'SYSTEM').length }; return summary; } catch (err) { warning(`Could not load instruction history: ${err.message}`); return { total: 0, high: 0, medium: 0, low: 0 }; } } /** * Run initial pressure check */ function runPressureCheck() { try { const output = execSync( 'node scripts/check-session-pressure.js --tokens 0/200000 --messages 1 --tasks 0', { encoding: 'utf8', stdio: 'pipe' } ); // Extract pressure level from output const levelMatch = output.match(/Pressure Level:\s+\[.*?m(.*?)\[/); const scoreMatch = output.match(/Overall Score:\s+([\d.]+)%/); return { level: levelMatch ? levelMatch[1] : 'NORMAL', score: scoreMatch ? parseFloat(scoreMatch[1]) : 0, output: output }; } catch (err) { error(`Pressure check failed: ${err.message}`); return { level: 'UNKNOWN', score: 0, output: '' }; } } /** * Check if local development server is running on port 9000 * ENFORCEMENT: Blocks session if not running during development work */ function checkLocalServer() { try { const output = execSync('lsof -i :9000 -t', { encoding: 'utf8', stdio: 'pipe' }); return output.trim().length > 0; } catch (err) { // lsof returns non-zero exit code if no process found return false; } } /** * Start local development server */ function startLocalServer() { try { log(' Attempting to start local server...', 'cyan'); execSync('npm start &', { encoding: 'utf8', stdio: 'inherit', detached: true }); // Wait for server to start let attempts = 0; while (attempts < 10) { if (checkLocalServer()) { return true; } execSync('sleep 1'); attempts++; } return false; } catch (err) { return false; } } /** * Main initialization */ async function main() { header('Tractatus Framework - Session Initialization'); // Check session status section('1. Checking Session Status'); const { isNew, isToday, sessionState } = isNewSession(); if (!isNew && sessionState) { log(` Session: ${sessionState.session_id}`, 'cyan'); log(` Messages: ${sessionState.message_count}`, 'cyan'); log(` Status: Continuing existing session`, 'yellow'); console.log(''); warning('This is a CONTINUED session - framework should already be active'); warning('If this is actually a NEW session, delete .claude/session-state.json'); } else { success('New session detected - initializing framework'); const newState = initializeSessionState(); log(` Session ID: ${newState.session_id}`, 'cyan'); } // Reset checkpoints for new day section('2. Resetting Token Checkpoints'); const checkpoints = resetTokenCheckpoints(); success(`Token budget: ${checkpoints.budget.toLocaleString()}`); success(`Next checkpoint: ${checkpoints.next_checkpoint.toLocaleString()} tokens (25%)`); // Load instruction history section('3. Loading Instruction History'); const instructions = loadInstructionHistory(); if (instructions.total === 0) { log(' No active instructions stored', 'yellow'); } else { success(`Active instructions: ${instructions.total}`); if (instructions.high > 0) { log(` HIGH persistence: ${instructions.high}`, 'cyan'); } if (instructions.medium > 0) { log(` MEDIUM persistence: ${instructions.medium}`, 'cyan'); } if (instructions.low > 0) { log(` LOW persistence: ${instructions.low}`, 'cyan'); } console.log(''); if (instructions.strategic > 0 || instructions.system > 0) { warning(`Critical instructions active (STRATEGIC: ${instructions.strategic}, SYSTEM: ${instructions.system})`); warning('These must be validated before conflicting actions'); } } // Run initial pressure check section('4. Running Initial Pressure Check'); const pressure = runPressureCheck(); success(`Pressure Level: ${pressure.level}`); success(`Overall Score: ${pressure.score}%`); // Framework component status section('5. Framework Components'); success('ContextPressureMonitor: ACTIVE'); success('InstructionPersistenceClassifier: READY'); success('CrossReferenceValidator: READY'); success('BoundaryEnforcer: READY'); success('MetacognitiveVerifier: READY (selective mode)'); success('PluralisticDeliberationOrchestrator: READY'); // Run framework tests section('6. Running Framework Tests'); try { log(' Running unit tests for Tractatus services...', 'cyan'); const testOutput = execSync( 'npm test -- --testPathPattern="tests/unit/(ContextPressureMonitor|InstructionPersistenceClassifier|CrossReferenceValidator|BoundaryEnforcer|MetacognitiveVerifier|PluralisticDeliberationOrchestrator)" 2>&1', { encoding: 'utf8' } ); // Extract test results const passMatch = testOutput.match(/Tests:\s+(\d+) passed/); const failMatch = testOutput.match(/(\d+) failed/); const totalMatch = testOutput.match(/(\d+) total/); if (failMatch && parseInt(failMatch[1]) > 0) { console.log(''); error(`Framework tests FAILED: ${failMatch[1]} failures`); error('Framework components are not functioning correctly - cannot proceed'); log(' Run: npm test -- --testPathPattern="tests/unit" for details', 'yellow'); console.log(''); error('Session initialization ABORTED due to test failures'); console.log(''); process.exit(1); // Exit with failure code } else if (passMatch) { success(`All framework tests passed (${passMatch[1]}/${totalMatch ? totalMatch[1] : passMatch[1]} tests)`); } else { warning('Could not parse test results - tests may have run successfully'); } } catch (err) { // Test failures throw non-zero exit code - this is a FAILURE condition const output = err.stdout || err.stderr || err.message; const passMatch = output.match(/Tests:\s+(\d+) passed/); const failMatch = output.match(/(\d+) failed/); // Check if tests actually passed despite stderr output if (passMatch && (!failMatch || parseInt(failMatch[1]) === 0)) { const totalMatch = output.match(/(\d+) total/); success(`All framework tests passed (${passMatch[1]}/${totalMatch ? totalMatch[1] : passMatch[1]} tests)`); return; // Tests passed, continue with init } console.log(''); if (failMatch && parseInt(failMatch[1]) > 0) { error(`Framework tests FAILED: ${failMatch[1]} failures`); error('Framework components are not functioning correctly - cannot proceed'); log(' Run: npm test -- --testPathPattern="tests/unit" to see failures', 'yellow'); } else { error('Framework tests encountered an error'); error(err.message); } console.log(''); error('Session initialization ABORTED due to test failures'); console.log(''); process.exit(1); // Exit with failure code } // CSP Compliance Scan section('7. CSP Compliance Scan (inst_008)'); try { const { scanForViolations, displayViolations } = require('./check-csp-violations'); const violations = scanForViolations(); if (violations.length === 0) { success('No CSP violations found in public files'); } else { error(`Found ${violations.length} CSP violation(s) in codebase`); console.log(''); // Group by file for summary const fileGroups = {}; violations.forEach(v => { fileGroups[v.file] = (fileGroups[v.file] || 0) + 1; }); Object.entries(fileGroups).forEach(([file, count]) => { log(` • ${file}: ${count} violation(s)`, 'yellow'); }); console.log(''); warning('Run: node scripts/check-csp-violations.js for details'); warning('Run: node scripts/fix-csp-violations.js to remediate'); console.log(''); } } catch (err) { warning(`Could not run CSP scan: ${err.message}`); } // ENFORCEMENT: Local development server check section('8. Development Environment Enforcement'); const localServerRunning = checkLocalServer(); if (!localServerRunning) { error('LOCAL DEVELOPMENT SERVER NOT RUNNING ON PORT 9000'); console.log(''); log(' ⚠️ MANDATORY REQUIREMENT:', 'bright'); log(' All development work MUST be tested locally before production deployment.', 'yellow'); log(' The local server on port 9000 is required for:', 'yellow'); log(' • Testing changes before deployment', 'cyan'); log(' • Verifying integrations work correctly', 'cyan'); log(' • Preventing production-first development', 'cyan'); log(' • Framework fade prevention', 'cyan'); console.log(''); log(' To fix:', 'bright'); log(' 1. Open a new terminal', 'cyan'); log(' 2. cd /home/theflow/projects/tractatus', 'cyan'); log(' 3. npm start', 'cyan'); log(' 4. Re-run: node scripts/session-init.js', 'cyan'); console.log(''); log(' Once the server is running, session-init will pass.', 'green'); console.log(''); error('SESSION BLOCKED: Start local server before proceeding'); console.log(''); process.exit(1); } success('Local development server running on port 9000'); success('Development environment ready'); // Hook Architecture Status section('9. Continuous Enforcement Architecture'); const hookValidatorsExist = fs.existsSync(path.join(__dirname, 'hook-validators')); if (hookValidatorsExist) { success('Hook validators installed (architectural enforcement)'); log(' • validate-file-edit.js: Enforces pre-action checks, CSP, conflicts', 'cyan'); log(' • validate-file-write.js: Prevents overwrites, enforces boundaries', 'cyan'); log(' • check-token-checkpoint.js: Prevents checkpoint fade', 'cyan'); console.log(''); log(' 📋 Pre-approved commands documented in PRE_APPROVED_COMMANDS.md', 'cyan'); log(' 🔍 Hook architecture prevents voluntary compliance failures', 'cyan'); } else { warning('Hook validators not yet installed'); log(' Hooks provide architectural enforcement beyond documentation', 'yellow'); } // Database Sync section('10. Syncing Instructions to Database'); try { log(' Synchronizing .claude/instruction-history.json to MongoDB...', 'cyan'); const { syncInstructions } = require('./sync-instructions-to-db.js'); // Run sync in silent mode (no verbose output) const syncResult = await syncInstructions(); if (syncResult && syncResult.success) { success(`Database synchronized: ${syncResult.finalCount} active rules`); if (syncResult.added > 0) { log(` Added: ${syncResult.added} new rules`, 'cyan'); } if (syncResult.updated > 0) { log(` Updated: ${syncResult.updated} existing rules`, 'cyan'); } if (syncResult.deactivated > 0) { log(` Deactivated: ${syncResult.deactivated} orphaned rules`, 'cyan'); } } else { warning('Database sync skipped or failed - admin UI may show stale data'); } } catch (err) { warning(`Database sync failed: ${err.message}`); log(' Admin UI may show outdated rule counts', 'yellow'); log(' Run: node scripts/sync-instructions-to-db.js --force to sync manually', 'yellow'); } // Summary header('Framework Initialization Complete'); console.log(''); log(' All 6 components are active and monitoring this session.', 'green'); console.log(''); log(' Next Actions:', 'bright'); log(' • Framework will monitor continuously', 'cyan'); log(' • Token checkpoint at 50,000 tokens (25%)', 'cyan'); log(' • Pressure checks at 25%, 50%, 75% milestones', 'cyan'); log(' • All instructions will be cross-referenced', 'cyan'); console.log(''); log(' ⚠️ MANDATORY REPORTING (Framework Discipline):', 'bright'); log(' • MUST report pressure to user at 50k, 100k, 150k tokens', 'yellow'); log(' • Format: "📊 Context Pressure: [LEVEL] ([SCORE]%) | Tokens: [X]/200000 | Next: [Y]"', 'yellow'); log(' • Failure to report = Framework fade (triggers recovery)', 'yellow'); console.log(''); log(' Claude: You may now proceed with session work.', 'green'); console.log(''); // Exit successfully process.exit(0); } // Run main().catch(err => { console.error(''); error(`Initialization failed: ${err.message}`); console.error(''); process.exit(1); });