#!/usr/bin/env node /** * Hook Validator: Token Checkpoint Enforcement * * Runs BEFORE any tool execution to enforce token checkpoint reporting. * Prevents "framework fade" where pressure checks are skipped. * * This is architectural enforcement - AI cannot bypass this check. * * Checks: * - Current token estimate vs next checkpoint * - If checkpoint overdue: BLOCK and force pressure check * * Exit codes: * 0 = PASS (allow tool execution) * 1 = FAIL (block - checkpoint overdue) * * Copyright 2025 Tractatus Project * Licensed under Apache License 2.0 */ const fs = require('fs'); const path = require('path'); const SESSION_STATE_PATH = path.join(__dirname, '../../.claude/session-state.json'); const TOKEN_CHECKPOINTS_PATH = path.join(__dirname, '../../.claude/token-checkpoints.json'); const INSTRUCTION_HISTORY_PATH = path.join(__dirname, '../../.claude/instruction-history.json'); /** * Color output */ const colors = { reset: '\x1b[0m', green: '\x1b[32m', yellow: '\x1b[33m', red: '\x1b[31m', cyan: '\x1b[36m' }; function log(message, color = 'reset') { console.log(`${colors[color]}${message}${colors.reset}`); } function error(message) { log(` āœ— ${message}`, 'red'); } function success(message) { log(` āœ“ ${message}`, 'green'); } /** * Estimate current token usage */ function estimateTokens() { try { if (!fs.existsSync(SESSION_STATE_PATH)) { return 0; } const sessionState = JSON.parse(fs.readFileSync(SESSION_STATE_PATH, 'utf8')); // If token_estimate exists in session state, use it if (sessionState.token_estimate && sessionState.token_estimate > 0) { return sessionState.token_estimate; } // Otherwise estimate from message count // Rough approximation: 1500 tokens per message (conservative) const messageCount = sessionState.message_count || 0; return messageCount * 1500; } catch (err) { // If we can't read session state, assume 0 (fail safe) return 0; } } /** * Load checkpoint configuration */ function loadCheckpoints() { try { if (!fs.existsSync(TOKEN_CHECKPOINTS_PATH)) { // No checkpoints file = no enforcement return null; } return JSON.parse(fs.readFileSync(TOKEN_CHECKPOINTS_PATH, 'utf8')); } catch (err) { return null; } } /** * Load and display HIGH persistence instructions * Helps refresh critical instructions at checkpoints */ function displayCriticalInstructions() { try { if (!fs.existsSync(INSTRUCTION_HISTORY_PATH)) { return; } const history = JSON.parse(fs.readFileSync(INSTRUCTION_HISTORY_PATH, 'utf8')); const activeInstructions = history.instructions?.filter(i => i.active) || []; // Filter for HIGH persistence instructions const highPersistence = activeInstructions.filter(i => i.persistence === 'HIGH'); // Filter for STRATEGIC and SYSTEM quadrants const strategic = highPersistence.filter(i => i.quadrant === 'STRATEGIC'); const system = highPersistence.filter(i => i.quadrant === 'SYSTEM'); if (highPersistence.length === 0) { return; } log(``, 'reset'); log(` šŸ“‹ INSTRUCTION PERSISTENCE REFRESH`, 'cyan'); log(` ─────────────────────────────────────────────────────────────`, 'cyan'); log(``, 'reset'); log(` At token checkpoints, HIGH persistence instructions must be validated:`, 'yellow'); log(``, 'reset'); // Display STRATEGIC instructions (values, ethics) if (strategic.length > 0) { log(` šŸŽÆ STRATEGIC (Values & Ethics) - ${strategic.length} active:`, 'cyan'); strategic.slice(0, 5).forEach((inst, idx) => { const preview = inst.instruction.length > 70 ? inst.instruction.substring(0, 67) + '...' : inst.instruction; log(` ${idx + 1}. [${inst.id}] ${preview}`, 'reset'); }); if (strategic.length > 5) { log(` ... and ${strategic.length - 5} more`, 'yellow'); } log(``, 'reset'); } // Display SYSTEM instructions (architectural constraints) if (system.length > 0) { log(` āš™ļø SYSTEM (Architectural Constraints) - ${system.length} active:`, 'cyan'); system.slice(0, 5).forEach((inst, idx) => { const preview = inst.instruction.length > 70 ? inst.instruction.substring(0, 67) + '...' : inst.instruction; log(` ${idx + 1}. [${inst.id}] ${preview}`, 'reset'); }); if (system.length > 5) { log(` ... and ${system.length - 5} more`, 'yellow'); } log(``, 'reset'); } log(` šŸ’” These instructions remain active and must be cross-referenced`, 'yellow'); log(` before conflicting actions.`, 'yellow'); log(``, 'reset'); } catch (err) { // Non-critical - don't fail checkpoint if instruction display fails } } /** * Check if checkpoint is overdue */ function checkOverdue(currentTokens, checkpoints) { if (!checkpoints) { return { overdue: false }; } // Find next incomplete checkpoint const nextCheckpoint = checkpoints.checkpoints.find(c => !c.completed); if (!nextCheckpoint) { // All checkpoints completed return { overdue: false }; } // Check if we've passed the checkpoint threshold const overdueThreshold = nextCheckpoint.tokens; const isOverdue = currentTokens >= overdueThreshold; return { overdue: isOverdue, checkpoint: nextCheckpoint, currentTokens: currentTokens, threshold: overdueThreshold, percentage: nextCheckpoint.percentage }; } /** * Main validation */ async function main() { // Estimate current token usage const currentTokens = estimateTokens(); // Load checkpoints const checkpoints = loadCheckpoints(); if (!checkpoints) { // No checkpoints configured - pass process.exit(0); } // Check if checkpoint overdue const status = checkOverdue(currentTokens, checkpoints); if (status.overdue) { log(`\nāš ļø TOKEN CHECKPOINT OVERDUE`, 'yellow'); log(``, 'reset'); error(`Current tokens: ~${status.currentTokens.toLocaleString()}`); error(`Checkpoint: ${status.threshold.toLocaleString()} tokens (${status.percentage}%)`); log(``, 'reset'); log(` šŸ“Š MANDATORY: Run pressure check before continuing`, 'red'); log(``, 'reset'); log(` Command:`, 'cyan'); log(` node scripts/check-session-pressure.js --tokens ${currentTokens}/200000 --messages auto`, 'cyan'); log(``, 'reset'); log(` This checkpoint enforces framework discipline and prevents fade.`, 'yellow'); log(``, 'reset'); // Display critical instructions to refresh working memory displayCriticalInstructions(); // Update checkpoints to mark as overdue try { checkpoints.overdue = true; checkpoints.last_check = new Date().toISOString(); fs.writeFileSync(TOKEN_CHECKPOINTS_PATH, JSON.stringify(checkpoints, null, 2)); } catch (err) { // Non-critical } process.exit(1); // BLOCK tool execution } // Checkpoint not overdue - allow execution process.exit(0); } main().catch(err => { error(`Checkpoint validation error: ${err.message}`); process.exit(1); });