tractatus/scripts/hook-validators/check-token-checkpoint.js
TheFlow d0ab1357f0 feat: implement continuous framework enforcement architecture
Implements architectural enforcement to prevent framework fade (voluntary
compliance failures). This addresses Case Study #27028 where AI skipped
session-init.js despite explicit CRITICAL warnings while implementing
anti-fade enforcement mechanisms.

## New Components

### Hook Validators (scripts/hook-validators/)
- validate-file-edit.js: Pre-Edit enforcement (CSP, conflicts, boundaries)
- validate-file-write.js: Pre-Write enforcement (overwrites, boundaries)
- check-token-checkpoint.js: Prevents checkpoint fade at 50k/100k/150k

### Documentation
- CONTINUOUS_ENFORCEMENT_ARCHITECTURE.md: Technical architecture
- BOOTSTRAPPING_SOLUTION.md: Solves auto-run session-init problem
- PRE_APPROVED_COMMANDS.md: Extracted from CLAUDE.md (context reduction)
- Case Study #27028: Framework fade during anti-fade implementation

### Session Initialization Enhancement
- scripts/session-init.js: Added Section 8 (Hook Architecture Status)
- Reports hook validator installation and pre-approved commands

### CLAUDE.md Reduction (Not Committed - .gitignored)
- Reduced from 235 lines to 86 lines (63% reduction)
- Philosophy: "If it can be enforced in code, it should not be documented"

## Key Findings

Case Study #27028 proved documentation-based governance fundamentally
cannot work. AI skipped session-init.js despite "⚠️ CRITICAL" warning
while actively implementing anti-fade enforcement. This validates the
thesis that architectural enforcement (code that runs automatically)
is the only viable solution.

## Next Steps

Bootstrapping solution required: session-init.js needs automatic
invocation on continued sessions. Without this, framework fade will
recur. Options documented in BOOTSTRAPPING_SOLUTION.md.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 19:55:12 +13:00

175 lines
4.4 KiB
JavaScript
Executable file

#!/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');
/**
* 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;
}
}
/**
* 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');
// 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);
});