#!/usr/bin/env node
// Downward Migration Tracker
// Tracks how many times each principle has been violated (corrections in its domain)
// Escalates enforcement level: context → reminder → enforced
//
// Levels:
// context — principle in rules file (advisory, always loaded)
// reminder — PreToolUse hook surfaces principle at moment of action
// enforced — PreToolUse hook blocks the action until confirmed
const fs = require('fs')
const path = require('path')
const PRINCIPLES_DATA = path.join(__dirname, 'data', 'principles.json')
const MIGRATION_DATA = path.join(__dirname, 'data', 'migration-state.json')
const CORRECTIONS_LOG = path.join(__dirname, 'corrections.log')
const REMINDER_THRESHOLD = 2 // corrections before escalating to reminder
const ENFORCE_THRESHOLD = 4 // corrections before escalating to enforced
function loadMigrationState () {
if (!fs.existsSync(MIGRATION_DATA)) return {}
try { return JSON.parse(fs.readFileSync(MIGRATION_DATA, 'utf8')) }
catch { return {} }
}
function saveMigrationState (state) {
const dir = path.dirname(MIGRATION_DATA)
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
fs.writeFileSync(MIGRATION_DATA, JSON.stringify(state, null, 2))
}
function loadPrinciples () {
if (!fs.existsSync(PRINCIPLES_DATA)) return []
return JSON.parse(fs.readFileSync(PRINCIPLES_DATA, 'utf8'))
}
function loadCorrections () {
if (!fs.existsSync(CORRECTIONS_LOG)) return []
return fs.readFileSync(CORRECTIONS_LOG, 'utf8')
.split('\n---\n')
.filter(e => e.includes('[correction]'))
}
function countViolations (principle, corrections) {
const strip = w => w.replace(/[^a-z0-9]/g, '')
const principleWords = principle.principle.toLowerCase().split(/\s+/).map(strip).filter(w => w.length > 2)
let count = 0
for (const c of corrections) {
const cWords = c.toLowerCase().split(/\s+/).map(strip).filter(w => w.length > 2)
const overlap = cWords.filter(w => principleWords.includes(w))
if (overlap.length >= 2) count++
}
return count
}
function determineLevel (violations) {
if (violations >= ENFORCE_THRESHOLD) return 'enforced'
if (violations >= REMINDER_THRESHOLD) return 'reminder'
return 'context'
}
function main () {
const principles = loadPrinciples()
const corrections = loadCorrections()
const state = loadMigrationState()
if (principles.length === 0) {
console.log('No principles loaded.')
return
}
console.log(`Principles: ${principles.length}`)
console.log(`Corrections: ${corrections.length}`)
console.log('')
const updates = []
for (const p of principles) {
if (p.status !== 'active') continue
const key = p.principle.slice(0, 60)
const violations = countViolations(p, corrections)
const currentLevel = state[key]?.level || 'context'
const newLevel = determineLevel(violations)
const changed = newLevel !== currentLevel
state[key] = {
level: newLevel,
violations,
lastChecked: new Date().toISOString()
}
const icon = newLevel === 'enforced' ? '🔒' : newLevel === 'reminder' ? '⚡' : '📄'
const arrow = changed ? ` ← was ${currentLevel}` : ''
console.log(` ${icon} [${newLevel}] ${key} (${violations} violations)${arrow}`)
if (changed) {
updates.push({ principle: p.principle, from: currentLevel, to: newLevel, violations })
}
}
saveMigrationState(state)
if (updates.length > 0) {
console.log('\n═══ LEVEL CHANGES ═══')
for (const u of updates) {
console.log(` ${u.from} → ${u.to}: "${u.principle.slice(0, 60)}" (${u.violations} violations)`)
}
console.log('\nTo apply enforcement changes, update hooks in .claude/settings.json')
console.log('Principles at "enforced" level should have PreToolUse hooks that block violations')
console.log('Principles at "reminder" level should have PreToolUse hooks that warn')
} else {
console.log('\nNo level changes.')
}
}
const command = process.argv[2]
if (command === 'status') {
const state = loadMigrationState()
if (Object.keys(state).length === 0) {
console.log('No migration state yet. Run without arguments to analyze.')
} else {
console.log('═══ ENFORCEMENT LEVELS ═══')
for (const [key, info] of Object.entries(state)) {
const icon = info.level === 'enforced' ? '🔒' : info.level === 'reminder' ? '⚡' : '📄'
console.log(` ${icon} [${info.level}] ${key} (${info.violations} violations)`)
}
}
} else {
main()
}