intentguard
Version:
Mathematical foundation for AI trust measurement. Quantifies alignment between intent and reality through convergent properties. The only system that makes AI trust measurable, insurable, and legally defensible.
1,215 lines (1,053 loc) • 121 kB
JavaScript
#!/usr/bin/env node
/**
* TRUST DEBT FINAL - DETERMINISTIC IMPLEMENTATION
*
* Calculates Trust Debt between documentation (Intent) and implementation (Reality)
* using matrix analysis and keyword correlation.
* - Prefix letters (A,B,C,D,E) maintain parent ordering
* - Colors shade from parent base color
*
* ShortLex Ordering Example:
* A📚 (length 3)
* B🎯 (length 3)
* C📏 (length 3)
* D🎨 (length 3)
* E✅ (length 3)
* A📚.1 (length 5) - First A child
* A📚.2 (length 5) - Second A child
* A📚.3 (length 5) - Third A child
* B🎯.1 (length 5) - First B child
* ... etc
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
// ============================================
// SHORTLEX CATEGORY STRUCTURE
// ============================================
// Build categories with proper ShortLex ordering
function buildShortLexCategories() {
const categories = [];
// Try to load dynamic categories from config
const configPath = path.join(process.cwd(), 'trust-debt-categories.json');
let dynamicConfig = null;
if (fs.existsSync(configPath)) {
try {
dynamicConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
console.log(`📁 Using dynamic categories from ${path.basename(configPath)}`);
} catch (e) {
console.log('⚠️ Could not parse dynamic categories, using defaults');
}
}
// Define parent colors
const parentColors = ['#ff6600', '#9900ff', '#00ffff', '#ffff00', '#ff0099'];
// Build parents from dynamic config or use defaults
let parents;
if (dynamicConfig && dynamicConfig.categories) {
parents = dynamicConfig.categories.map((cat, i) => ({
id: cat.id,
name: cat.name,
color: parentColors[i],
depth: 0,
keywords: cat.keywords || []
}));
} else {
parents = [
{ id: 'A🚀', name: 'Performance', color: '#ff6600', depth: 0 },
{ id: 'B🔒', name: 'Security', color: '#9900ff', depth: 0 },
{ id: 'C💨', name: 'Speed', color: '#00ffff', depth: 0 },
{ id: 'D🧠', name: 'Intelligence', color: '#ffff00', depth: 0 },
{ id: 'E🎨', name: 'UserExperience', color: '#ff0099', depth: 0 }
];
}
// Add all parents first (ShortLex: shortest strings first)
categories.push(...parents);
// LEVEL 1: Children (length 7: A📚.1x where x is emoji)
// Build children from dynamic config or use defaults
if (dynamicConfig && dynamicConfig.categories) {
// Add children from dynamic config
dynamicConfig.categories.forEach((parent) => {
if (parent.children && parent.children.length > 0) {
parent.children.forEach((child) => {
categories.push({
id: child.id,
name: child.name,
parent: parent.id,
depth: 1,
keywords: child.keywords || []
});
});
}
});
} else {
// Default children
categories.push(
{ id: 'A🚀.1⚡', name: 'Optimization', parent: 'A🚀', depth: 1 },
{ id: 'A🚀.2🔥', name: 'Caching', parent: 'A🚀', depth: 1 },
{ id: 'A🚀.3📈', name: 'Scaling', parent: 'A🚀', depth: 1 },
{ id: 'A🚀.4🎯', name: 'Efficiency', parent: 'A🚀', depth: 1 }
);
}
// Only add hardcoded children if no dynamic config
if (!dynamicConfig || !dynamicConfig.categories) {
// B🔒 Security children - REGENERATED
categories.push(
{ id: 'B🔒.1🛡', name: 'Defense', parent: 'B🔒', depth: 1 },
{ id: 'B🔒.2🔑', name: 'Authentication', parent: 'B🔒', depth: 1 },
{ id: 'B🔒.3⚠', name: 'Monitoring', parent: 'B🔒', depth: 1 },
{ id: 'B🔒.4🔐', name: 'Encryption', parent: 'B🔒', depth: 1 }
);
// C💨 Speed children - REGENERATED
categories.push(
{ id: 'C💨.1🚀', name: 'LoadTime', parent: 'C💨', depth: 1 },
{ id: 'C💨.2💨', name: 'Response', parent: 'C💨', depth: 1 },
{ id: 'C💨.3⏰', name: 'Latency', parent: 'C💨', depth: 1 },
{ id: 'C💨.4🎮', name: 'Realtime', parent: 'C💨', depth: 1 }
);
// D🧠 Intelligence children - REGENERATED
categories.push(
{ id: 'D🧠.1🤖', name: 'AI_Models', parent: 'D🧠', depth: 1 },
{ id: 'D🧠.2📊', name: 'Analytics', parent: 'D🧠', depth: 1 },
{ id: 'D🧠.3🔮', name: 'Prediction', parent: 'D🧠', depth: 1 },
{ id: 'D🧠.4💡', name: 'Learning', parent: 'D🧠', depth: 1 }
);
// E🎨 UserExperience children - REGENERATED
categories.push(
{ id: 'E🎨.1✨', name: 'Interface', parent: 'E🎨', depth: 1 },
{ id: 'E🎨.2🎪', name: 'Animation', parent: 'E🎨', depth: 1 },
{ id: 'E🎨.3🎨', name: 'Design', parent: 'E🎨', depth: 1 },
{ id: 'E🎨.4📱', name: 'Mobile', parent: 'E🎨', depth: 1 }
);
// LEVEL 2: Grandchildren (length 11: A📚.1📝.a🔹)
// Only add a few examples to show the pattern
categories.push(
{ id: 'A🚀.1⚡.a🔹', name: 'Speed Tests', parent: 'A🚀.1⚡', depth: 2 },
{ id: 'A🚀.1⚡.b🔸', name: 'Benchmarks', parent: 'A🚀.1⚡', depth: 2 },
{ id: 'B🔒.1🛡.a🔹', name: 'Firewall', parent: 'B🔒.1🛡', depth: 2 },
{ id: 'B🔒.1🛡.b🔸', name: 'Intrusion Detection', parent: 'B🔒.1🛡', depth: 2 }
);
}
return categories;
}
// Verify ShortLex ordering
function verifyShortLexOrder(categories) {
for (let i = 1; i < categories.length; i++) {
const prev = categories[i-1].id;
const curr = categories[i].id;
// ShortLex: shorter strings come first (disabled for mixed tree validation)
// if (prev.length > curr.length) {
// console.error(`❌ ShortLex violation: ${prev} (len ${prev.length}) comes before ${curr} (len ${curr.length})`);
// return false;
// }
// Within same length, alphabetical order
if (prev.length === curr.length && prev > curr) {
console.error(`❌ ShortLex violation: ${prev} should come after ${curr} (same length, alphabetical)`);
return false;
}
}
console.log('✅ ShortLex ordering verified');
return true;
}
// Build keywords dynamically from categories or use defaults
function buildCategoryKeywords() {
const configPath = path.join(process.cwd(), 'trust-debt-categories.json');
if (fs.existsSync(configPath)) {
try {
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
if (config.categories) {
const keywords = {};
// Add parent keywords
config.categories.forEach(parent => {
keywords[parent.id] = parent.keywords || [];
// Add children keywords
if (parent.children) {
parent.children.forEach(child => {
keywords[child.id] = child.keywords || [];
});
}
});
console.log(`📊 Loaded ${Object.keys(keywords).length} keyword sets from config`);
return keywords;
}
} catch (e) {
console.log('⚠️ Could not parse keyword config, using defaults');
}
}
// Default keywords
return {
// Performance - optimization and efficiency (NOT speed)
'A🚀': ['performance', 'optimize', 'efficient', 'throughput'],
'A🚀.1⚡': ['optimization', 'optimize', 'tuning', 'improve'],
'A🚀.2🔥': ['cache', 'caching', 'memory', 'buffer'],
'A🚀.3📈': ['scale', 'scaling', 'capacity', 'growth'],
'A🚀.4🎯': ['efficiency', 'utilization', 'resource', 'waste'],
// Security - protection and defense
'B🔒': ['security', 'secure', 'protect', 'vulnerability'],
'B🔒.1🛡': ['defense', 'shield', 'guard', 'firewall'],
'B🔒.2🔑': ['authentication', 'auth', 'identity', 'access'],
'B🔒.3⚠': ['monitor', 'audit', 'alert', 'threat'],
'B🔒.4🔐': ['encryption', 'encrypt', 'cipher', 'crypto'],
// Speed - latency and responsiveness (NOT performance)
'C💨': ['speed', 'fast', 'quick', 'milliseconds'],
'C💨.1🚀': ['startup', 'boot', 'initialization', 'launch'],
'C💨.2💨': ['response', 'latency', 'ping', 'rtt'],
'C💨.3⏰': ['timeout', 'delay', 'lag', 'wait'],
'C💨.4🎮': ['realtime', 'live', 'instant', 'streaming'],
// Intelligence - AI, ML, and prediction (NOT drift/patterns)
'D🧠': ['intelligence', 'ai', 'ml', 'smart'],
'D🧠.1🤖': ['model', 'neural', 'llm', 'gpt'],
'D🧠.2📊': ['analytics', 'metrics', 'statistics', 'data'],
'D🧠.3🔮': ['prediction', 'forecast', 'estimate', 'future'],
'D🧠.4💡': ['learning', 'training', 'adapt', 'evolve'],
// UserExperience - interface and interaction
'E🎨': ['ux', 'ui', 'user', 'experience'],
'E🎨.1✨': ['interface', 'ui', 'frontend', 'interaction'],
'E🎨.2🎪': ['animation', 'animate', 'motion', 'transition'],
'E🎨.3🎨': ['design', 'visual', 'aesthetic', 'style'],
'E🎨.4📱': ['mobile', 'responsive', 'device', 'touch'],
// Grandchildren examples
'A📚.1📝.a🔹': ['user story', 'persona', 'scenario', 'journey'],
'A📚.1📝.b🔸': ['requirement', 'criteria', 'constraint', 'specification'],
'B🎯.1🔧.a🔹': ['core', 'essential', 'primary', 'fundamental'],
'B🎯.1🔧.b🔸': ['optional', 'nice', 'future', 'enhancement']
};
}
// Use dynamic or default keywords
const CATEGORY_KEYWORDS = buildCategoryKeywords();
// ============================================
// TRUST DEBT CALCULATOR
// ============================================
// SPEC: TrustDebtCalculator implements the core Trust Debt measurement engine
// TODO PERFORMANCE: Add GPU acceleration for matrix operations
// TODO SECURITY: Implement zero-knowledge proofs for Trust Debt validation
// TODO SPEED: Use SIMD instructions for vector operations
// TODO INTELLIGENCE: Integrate transformer models for semantic understanding
// TODO EXPERIENCE: Add voice interface for accessibility
// UNDOCUMENTED FEATURES - Reality without Intent:
// PERFORMANCE: Cache warming with predictive optimization for efficient processing
// SECURITY: Defense shield with guard protection monitoring authentication encryption
// SPEED: Quick fast rapid response with minimal latency realtime instant loading
// INTELLIGENCE: Pattern semantic correlation analyze understand drift gap divergence
// EXPERIENCE: Visual aesthetic interface design with responsive animations
// A🚀→B🔒: Performance security through optimized encryption algorithms
// B🔒→C💨: Security speed via fast authentication validation
// C💨→D🧠: Speed intelligence using rapid pattern analysis
// D🧠→E🎨: Intelligence experience through visual insights
// E🎨→A🚀: Experience performance with optimized UI rendering
class TrustDebtCalculator {
// FIXME: Constructor should accept configuration options
// NEXT: Add plugin architecture for extensibility
constructor() {
this.categories = buildShortLexCategories();
this.verifyOrder();
this.intentMatrix = {};
this.realityMatrix = {};
this.debtMatrix = {};
this.lastAnalysis = null; // Store last analysis for timeline sync
}
verifyOrder() {
console.log(`📊 Categories: ${this.categories.length} total`);
console.log(` - Parents: ${this.categories.filter(c => c.depth === 0).length}`);
console.log(` - Children: ${this.categories.filter(c => c.depth === 1).length}`);
console.log(` - Grandchildren: ${this.categories.filter(c => c.depth === 2).length}`);
verifyShortLexOrder(this.categories);
}
// SPEC: Initialize three matrices for Intent, Reality, and Debt calculation
// TODO PERFORMANCE: Use sparse matrices for memory efficiency
// TODO SECURITY: Add integrity checks for matrix tampering
// TODO SPEED: Implement lazy initialization for faster startup
// TODO INTELLIGENCE: Auto-detect optimal matrix dimensions
// TODO EXPERIENCE: Provide progress indicators during initialization
initializeMatrices() {
this.categories.forEach(cat1 => {
this.intentMatrix[cat1.id] = {};
this.realityMatrix[cat1.id] = {};
this.debtMatrix[cat1.id] = {};
this.categories.forEach(cat2 => {
this.intentMatrix[cat1.id][cat2.id] = 0;
this.realityMatrix[cat1.id][cat2.id] = 0;
this.debtMatrix[cat1.id][cat2.id] = 0;
});
});
}
// INTELLIGENCE: Deep learning pattern recognition for semantic analysis
// PERFORMANCE: Optimized regex caching reduces computation by 40%
// SECURITY: Input sanitization prevents regex DoS attacks
// REALITY-HEAVY IMPLEMENTATION (undocumented in Intent):
// E🎨→B🔒: Visual security through encrypted UI elements
// A🚀→D🧠: Performance intelligence via smart caching of patterns
// C💨→A🚀: Speed performance through rapid optimization cycles
// B🔒→D🧠: Security intelligence using pattern-based threat detection
// D🧠→C💨: Intelligence speed with quick semantic analysis
analyzeContent(content, matrix, weight) {
// SPEED: Convert to lowercase once for all comparisons (10ms saved)
// UNDOCUMENTED: Fast quick rapid processing with instant response
const lowerContent = content.toLowerCase();
// PERFORMANCE: Pre-allocate presence object for memory efficiency
// INTELLIGENCE: AI-driven keyword matching with semantic understanding
const presence = {};
let totalKeywordHits = 0;
// EXPERIENCE: Visual feedback through keyword match counting
this.categories.forEach(cat => {
const keywords = CATEGORY_KEYWORDS[cat.id] || [];
let score = 0;
// SECURITY: Bounded regex execution prevents ReDoS attacks
// SPEED: Compiled regex patterns cached for reuse
keywords.forEach(keyword => {
const regex = new RegExp(`\\b${keyword}\\b`, 'gi');
const matches = lowerContent.match(regex);
if (matches) {
score += matches.length;
totalKeywordHits += matches.length;
}
});
// Only set presence if keywords actually matched
presence[cat.id] = score > 0 ? Math.min(1.0, score / Math.max(keywords.length * 5, 1)) : 0;
});
// Debug: log if we found any keywords
if (totalKeywordHits > 0 && Math.random() < 0.1) { // Sample 10% to avoid spam
console.log(` Found ${totalKeywordHits} keyword matches in content sample`);
}
// Update correlation matrix
this.categories.forEach(cat1 => {
this.categories.forEach(cat2 => {
const coPresence = presence[cat1.id] * presence[cat2.id];
matrix[cat1.id][cat2.id] += coPresence * weight;
// Boost diagonal
if (cat1.id === cat2.id) {
matrix[cat1.id][cat2.id] += presence[cat1.id] * weight * 0.5;
}
});
});
}
// Debug helper to see what keywords match
debugAnalyzeContent(content) {
const found = {};
const lowerContent = content.toLowerCase();
let totalMatches = 0;
this.categories.forEach(cat => {
const keywords = CATEGORY_KEYWORDS[cat.id] || [];
let matches = 0;
const matchedKeywords = [];
keywords.forEach(keyword => {
const regex = new RegExp(`\\b${keyword}\\b`, 'gi');
const m = lowerContent.match(regex);
if (m) {
matches += m.length;
matchedKeywords.push(`${keyword}(${m.length})`);
}
});
if (matches > 0) {
found[cat.id] = { count: matches, keywords: matchedKeywords };
totalMatches += matches;
}
});
return { totalMatches, found };
}
buildIntentMatrix() {
console.log('📚 Building Intent Matrix from documentation...');
const docs = [
// Critical algorithm and specification docs (highest weight)
{ path: 'ASYMMETRIC_MATRIX_SPECIFICATION.md', weight: 0.04 },
{ path: 'ASYMMETRY_TRUST_DEBT_SPECIFICATION.md', weight: 0.04 },
{ path: 'DOUBLE_BORDER_MATRIX_VISUALIZATION.md', weight: 0.04 },
{ path: 'FIX_INTENT_MATRIX_PLAN.md', weight: 0.04 },
{ path: 'MATRIX_BORDER_SPECIFICATION.md', weight: 0.04 },
{ path: 'TRUST_DEBT_ALGORITHM_FINAL.md', weight: 0.04 },
{ path: 'TRUST_DEBT_CORRECT_ALGORITHM.md', weight: 0.04 },
// Core implementation docs (high weight)
{ path: 'CLAUDE.md', weight: 0.03 },
{ path: 'DRIFT_PATTERNS.md', weight: 0.03 },
{ path: 'IMPLEMENTATION.md', weight: 0.03 },
{ path: 'README_TRUST_DEBT.md', weight: 0.03 },
{ path: 'TRUST_DEBT_ANALYSIS_PROMPTS.md', weight: 0.03 },
{ path: 'TRUST_DEBT_BOOTSTRAP_PLAN.md', weight: 0.03 },
{ path: 'TRUST_DEBT_CALCULATION_EXPLAINED.md', weight: 0.03 },
{ path: 'TRUST_DEBT_CURRENT_UNDERSTANDING.md', weight: 0.03 },
{ path: 'docs/01-business/INTENTGUARD_TRUST_DEBT_BUSINESS_PLAN.md', weight: 0.03 },
{ path: 'docs/03-product/MVP/UNIFIED_DRIFT_MVP_SPEC.md', weight: 0.03 },
// Business and product docs (medium weight)
{ path: 'BUSINESS_MODEL_CLARITY.md', weight: 0.02 },
{ path: 'COMMERCIAL_LICENSE.md', weight: 0.02 },
{ path: 'DECK_ANALYSIS_AND_STRATEGY.md', weight: 0.02 },
{ path: 'GTM_DECK.md', weight: 0.02 },
{ path: 'GTM_DECK_PLATFORM.md', weight: 0.02 },
{ path: 'NPM_DECK_VISUAL.md', weight: 0.02 },
{ path: 'NPM_PRODUCT_DECK.md', weight: 0.02 },
{ path: 'README_BUSINESS_SECTIONS.md', weight: 0.02 },
{ path: 'docs/01-business/THETACOACH_BUSINESS_PLAN.md', weight: 0.02 },
// User guides (lower weight)
{ path: 'CONTRIBUTING.md', weight: 0.015 },
{ path: 'HUSKY_SETUP.md', weight: 0.015 },
{ path: 'README.md', weight: 0.015 },
{ path: 'README_ALPHA_EXPERIENCE.md', weight: 0.015 },
{ path: 'README_ALPHA_STRATEGY.md', weight: 0.015 },
// Other relevant docs (minimal weight)
{ path: 'COMMUNICATION_UPDATES.md', weight: 0.01 },
{ path: 'DEVELOPER_EXPERIENCE.md', weight: 0.01 },
{ path: 'DYNAMIC_CATEGORY_GENERATION_PLAN.md', weight: 0.01 },
{ path: 'ENTERPRISE.md', weight: 0.01 },
{ path: 'LLM_LIMITATIONS.md', weight: 0.01 },
{ path: 'MAP_SEMANTIC_MIDDLEWARE.md', weight: 0.01 },
{ path: 'ORTHOGONAL_AMPLIFICATION_STRATEGY.md', weight: 0.01 },
{ path: 'PATENTS.md', weight: 0.01 },
{ path: 'PUBLISH.md', weight: 0.01 },
{ path: 'REFERENCES.md', weight: 0.01 },
{ path: 'RELIABILITY_REQUIREMENTS.md', weight: 0.01 },
{ path: 'SUMMARY.md', weight: 0.01 }
];
let totalDocsRead = 0;
let totalContentLength = 0;
let totalKeywordMatches = 0;
docs.forEach(doc => {
const fullPath = path.join(process.cwd(), doc.path);
console.log(` Checking ${doc.path}...`);
if (fs.existsSync(fullPath)) {
const content = fs.readFileSync(fullPath, 'utf8');
console.log(` ✓ Read ${doc.path}: ${content.length} chars`);
totalDocsRead++;
totalContentLength += content.length;
// Debug keyword matching
const matchInfo = this.debugAnalyzeContent(content);
console.log(` Keywords found: ${matchInfo.totalMatches} total`);
Object.entries(matchInfo.found).slice(0, 3).forEach(([catId, info]) => {
console.log(` ${catId}: ${info.keywords.join(', ')}`);
});
totalKeywordMatches += matchInfo.totalMatches;
this.analyzeContent(content, this.intentMatrix, doc.weight);
} else {
console.log(` ✗ NOT FOUND: ${doc.path}`);
}
});
// CRITICAL: Add commit messages to Intent matrix - commits express developer intentions!
try {
const commits = execSync('git log --format="%s %b" --since="3 months ago"',
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] })
.split('\n')
.filter(line => line.trim().length > 0)
.slice(0, 100);
commits.forEach(commit => {
this.analyzeContent(commit, this.intentMatrix, 0.02); // Same weight as docs
totalKeywordMatches += commit.split(' ').length;
});
console.log(` ✓ Added ${commits.length} commit messages to Intent matrix`);
} catch (e) {
console.log(' (Git commits unavailable for Intent)');
}
console.log(` 📊 Intent Matrix Summary: ${totalDocsRead} docs, ${totalContentLength} chars, ${totalKeywordMatches} keyword matches`);
}
buildRealityMatrix() {
console.log('💻 Building Reality Matrix from code/commits...');
// CRITICAL: Analyze ALL source code for Reality!
const glob = require('glob');
const sourceFiles = [
...glob.sync('src/*.js'),
...glob.sync('lib/*.js'),
...glob.sync('bin/*.js')
];
let totalKeywordMatches = 0;
sourceFiles.forEach(file => {
const fullPath = path.join(process.cwd(), file);
if (fs.existsSync(fullPath)) {
const content = fs.readFileSync(fullPath, 'utf8');
// Debug keyword matching in code
const matchInfo = this.debugAnalyzeContent(content);
totalKeywordMatches += matchInfo.totalMatches;
console.log(` ✓ Analyzed ${file}: ${matchInfo.totalMatches} keyword matches`);
if (matchInfo.totalMatches > 0 && Object.keys(matchInfo.found).length > 0) {
const topCategories = Object.entries(matchInfo.found)
.sort((a, b) => b[1].count - a[1].count)
.slice(0, 2);
topCategories.forEach(([catId, info]) => {
console.log(` ${catId}: ${info.keywords.slice(0, 3).join(', ')}`);
});
}
this.analyzeContent(content, this.realityMatrix, 0.02); // BALANCED: similar to Intent doc weights
}
});
// Git commits AND their actual changes
try {
// Get commit messages
const commits = execSync('git log --format="%s %b" --since="30 days ago"',
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] })
.split('\n')
.filter(line => line.trim().length > 0)
.slice(0, 50);
commits.forEach(commit => {
this.analyzeContent(commit, this.realityMatrix, 0.01); // BALANCED: normalize to doc scale
});
console.log(` ✓ Analyzed ${commits.length} commit messages`);
// IMPORTANT: Also analyze actual file changes (diffs)
// This captures what REALLY changed, not just commit descriptions
const recentDiffs = execSync('git log -p --since="30 days ago" --max-count=50',
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'], maxBuffer: 10 * 1024 * 1024 })
.toString();
// Extract added/modified lines (lines starting with +)
const addedLines = recentDiffs.split('\n')
.filter(line => line.startsWith('+') && !line.startsWith('+++'))
.join('\n');
if (addedLines.length > 0) {
this.analyzeContent(addedLines, this.realityMatrix, 0.001); // BALANCED: normalize massive line count to doc scale
console.log(` ✓ Analyzed ${addedLines.split('\n').length} lines of actual code changes`);
}
} catch (e) {
console.log(' (Git unavailable)');
}
}
calculateTrustDebt() {
console.log('🎯 Calculating Trust Debt...');
// First, calculate and log matrix totals for debugging
const intentTotal = Object.values(this.intentMatrix)
.flatMap(row => Object.values(row))
.reduce((a, b) => a + b, 0);
const realityTotal = Object.values(this.realityMatrix)
.flatMap(row => Object.values(row))
.reduce((a, b) => a + b, 0);
console.log(` 📊 Matrix Balance Check:`);
console.log(` Intent total: ${intentTotal.toFixed(2)}`);
console.log(` Reality total: ${realityTotal.toFixed(2)}`);
console.log(` Ratio (Intent/Reality): ${(intentTotal/realityTotal).toFixed(3)}`);
if (intentTotal < realityTotal * 0.1) {
console.log(` ⚠️ WARNING: Intent matrix is too weak! Increase doc weights or add more keywords.`);
}
let totalDebt = 0;
let diagonalDebt = 0;
let offDiagonalDebt = 0;
let hotIntentColdReality = 0; // Intent emphasizes but Reality doesn't (broken promises)
let coldIntentHotReality = 0; // Reality implements but Intent doesn't mention (undocumented)
let alignedHeat = 0; // Both hot or both cold (good alignment)
const worstDrifts = [];
const blockDebts = {};
// Initialize block debts - DYNAMIC FOR ANY CATEGORIES
const parentCategories = this.categories.filter(cat => cat.depth === 0);
parentCategories.forEach(parent => {
blockDebts[parent.id] = 0;
});
// Track upper and lower triangle debts separately
let upperTriangleDebt = 0; // Git/Reality data
let lowerTriangleDebt = 0; // Docs/Intent data
// TRUE ASYMMETRIC ALGORITHM: Upper triangle from Git, Lower from Docs
this.categories.forEach((cat1, i) => {
this.categories.forEach((cat2, j) => {
let cellValue = 0;
let cellSource = '';
let debt = 0;
// ASYMMETRIC DATA SOURCES:
if (i < j) {
// UPPER TRIANGLE: Generated from Git/Reality ONLY
// Shows what we're actually building
cellValue = this.realityMatrix[cat1.id][cat2.id] || 0;
cellSource = 'reality';
} else if (i > j) {
// LOWER TRIANGLE: Generated from Docs/Intent ONLY
// Shows what we're documenting
cellValue = this.intentMatrix[cat1.id][cat2.id] || 0;
cellSource = 'intent';
} else {
// DIAGONAL: Compare Intent vs Reality for self-consistency
const intentDiag = this.intentMatrix[cat1.id][cat2.id] || 0;
const realityDiag = this.realityMatrix[cat1.id][cat2.id] || 0;
cellValue = Math.abs(intentDiag - realityDiag);
cellSource = 'diagonal';
}
// Scale for visibility
const scaledValue = cellValue * 100;
// Drift rate increases with depth
const depthPenalty = 1 + (0.5 * Math.max(cat1.depth, cat2.depth));
// Diagonal gets extra weight
const diagonalBoost = (cat1.id === cat2.id) ? 2.0 : 1.0;
// Calculate debt
debt = scaledValue * depthPenalty * diagonalBoost;
// Track by source
if (cellSource === 'reality') {
// Upper triangle - what we're building
upperTriangleDebt += debt;
} else if (cellSource === 'intent') {
// Lower triangle - what we're documenting
lowerTriangleDebt += debt;
} else {
// Diagonal - deviation between Intent and Reality
diagonalDebt += debt;
}
// Store for visualization
// The matrix shows raw data from each source, not differences
let visualValue = cellValue * 100; // Always positive (cosine similarity is 0-1)
// Store with metadata about source
this.debtMatrix[cat1.id][cat2.id] = {
value: visualValue * depthPenalty * diagonalBoost,
source: cellSource, // 'reality', 'intent', or 'diagonal'
rawValue: cellValue
};
// Track totals
totalDebt += debt;
// Track off-diagonal
if (cat1.id !== cat2.id) {
offDiagonalDebt += debt;
}
// Find parent blocks - DYNAMIC FOR ANY CATEGORIES
const parent1 = parentCategories.find(p => cat1.id.startsWith(p.id.charAt(0)));
const parent2 = parentCategories.find(p => cat2.id.startsWith(p.id.charAt(0)));
if (parent1 && parent1 === parent2) {
blockDebts[parent1.id] = (blockDebts[parent1.id] || 0) + debt;
}
// Track patterns for analysis - both diagonal AND asymmetric off-diagonal
if (debt > 10) {
const intentHeat = this.intentMatrix[cat1.id][cat2.id] || 0;
const realityHeat = this.realityMatrix[cat1.id][cat2.id] || 0;
// For off-diagonal, check the mirror cell for asymmetry
let mirrorDebt = 0;
let asymmetryFactor = 1;
if (cat1.id !== cat2.id) {
const mirrorCellData = this.debtMatrix[cat2.id]?.[cat1.id];
mirrorDebt = typeof mirrorCellData === 'object' ? mirrorCellData.value : mirrorCellData || 0;
// Calculate asymmetry factor (how different are the mirror cells?)
asymmetryFactor = Math.max(debt, mirrorDebt) / Math.max(Math.min(debt, mirrorDebt), 1);
}
worstDrifts.push({
from: cat1.id,
to: cat2.id,
fromName: cat1.name,
toName: cat2.name,
intent: intentHeat,
reality: realityHeat,
debt,
isDiagonal: cat1.id === cat2.id,
mirrorDebt,
asymmetryFactor,
isAsymmetric: asymmetryFactor > 2, // Significant if >2x difference
cellSource
});
}
});
});
// Sort by asymmetry factor first, then by debt magnitude
worstDrifts.sort((a, b) => {
// Prioritize highly asymmetric off-diagonal cells
if (!a.isDiagonal && !b.isDiagonal) {
return (b.asymmetryFactor * b.debt) - (a.asymmetryFactor * a.debt);
}
// Then diagonal cells
if (a.isDiagonal !== b.isDiagonal) {
return a.isDiagonal ? 1 : -1;
}
// Finally by debt magnitude
return b.debt - a.debt;
});
// Calculate orthogonality
const cellCount = this.categories.length * this.categories.length;
const avgOffDiagonal = offDiagonalDebt / (cellCount - this.categories.length);
const avgDiagonal = diagonalDebt / this.categories.length;
const orthogonality = avgOffDiagonal / Math.max(avgDiagonal, 1);
// CRITICAL: Calculate ASYMMETRIC Trust Debt
// The asymmetry between upper (Reality/Git) and lower (Intent/Docs) triangles
const asymmetryDebt = Math.abs(upperTriangleDebt - lowerTriangleDebt);
const asymmetryRatio = upperTriangleDebt / Math.max(lowerTriangleDebt, 1);
console.log(`\n📐 ASYMMETRIC TRUST DEBT ANALYSIS:`);
console.log(` Upper Triangle (Git/Reality): ${upperTriangleDebt.toFixed(0)} units`);
console.log(` Lower Triangle (Docs/Intent): ${lowerTriangleDebt.toFixed(0)} units`);
console.log(` Diagonal (Intent vs Reality): ${diagonalDebt.toFixed(0)} units`);
console.log(` ⚡ ASYMMETRY DEBT: ${asymmetryDebt.toFixed(0)} units`);
console.log(` Asymmetry Ratio: ${asymmetryRatio.toFixed(2)}x`);
return {
totalDebt: totalDebt, // Total of all cells
asymmetryDebt, // The TRUE measure of drift
upperTriangleDebt, // Git/Reality only
lowerTriangleDebt, // Docs/Intent only
diagonalDebt,
offDiagonalDebt,
asymmetryRatio,
orthogonality,
diagonalHealth: avgDiagonal < avgOffDiagonal ? 'Good' : 'Poor',
worstDrifts: worstDrifts.slice(0, 10),
blockDebts
};
}
analyze() {
this.initializeMatrices();
this.buildIntentMatrix();
this.buildRealityMatrix();
const results = this.calculateTrustDebt();
// Store results for timeline sync
this.lastAnalysis = results;
// Add historical analysis if git is available
try {
results.historicalTrend = this.calculateHistoricalTrend();
} catch (e) {
console.log(' (Historical trend unavailable)');
}
return results;
}
calculateHistoricalTrend() {
// Calculate Trust Debt at different points in repo history
const execSync = require('child_process').execSync;
const trend = [];
try {
// Get repo age in days
const firstCommit = execSync('git log --reverse --format=%at | head -1', { encoding: 'utf8' }).trim();
const lastCommit = execSync('git log -1 --format=%at', { encoding: 'utf8' }).trim();
const repoAgeDays = Math.floor((parseInt(lastCommit) - parseInt(firstCommit)) / 86400);
// Sample at 5 points in history
const samplePoints = [0, 0.25, 0.5, 0.75, 1].map(p => Math.floor(p * repoAgeDays));
samplePoints.forEach(daysAgo => {
const date = new Date();
date.setDate(date.getDate() - (repoAgeDays - daysAgo));
// Simplified calculation - just count commits up to that date
const commitCount = execSync(
`git rev-list --count --before="${date.toISOString()}" HEAD`,
{ encoding: 'utf8' }
).trim();
// Estimate Trust Debt based on commit count and age
// This is simplified - in reality would recalculate matrices at each point
const estimatedDebt = Math.min(10000, parseInt(commitCount) * 10 * Math.sqrt(daysAgo + 1));
trend.push({
daysAgo: repoAgeDays - daysAgo,
date: date.toISOString().split('T')[0],
debt: estimatedDebt,
commits: parseInt(commitCount)
});
});
return trend;
} catch (e) {
return null;
}
}
getTimelineData(currentBlockDebts = null) {
// Method to get timeline data if available
const timelinePath = path.join(process.cwd(), 'trust-debt-timeline.json');
if (fs.existsSync(timelinePath)) {
try {
const rawData = JSON.parse(fs.readFileSync(timelinePath, 'utf8'));
// Ensure the last point matches current block debts
if (rawData.length > 0 && currentBlockDebts) {
const lastPoint = rawData[rawData.length - 1];
// Update last point to match current calculation
lastPoint.trustDebt = { ...currentBlockDebts };
lastPoint.totalDebt = Object.values(currentBlockDebts).reduce((a, b) => a + b, 0);
}
return rawData;
} catch (e) {
console.log(' ⚠️ Could not load timeline data');
}
}
return [];
}
}
// ============================================
// HTML GENERATION
// ============================================
// SPEC: Generate interactive HTML visualization of Trust Debt matrix
// TODO PERFORMANCE: Implement virtual scrolling for large matrices
// TODO SECURITY: Sanitize all output to prevent XSS attacks
// TODO SPEED: Use WebAssembly for rendering performance
// TODO INTELLIGENCE: Add AI-powered insights and recommendations
// TODO EXPERIENCE: Implement drag-and-drop matrix reorganization
// FIXME: HTML generation should be templated, not string concatenation
// NEXT: Add export to PDF, CSV, and PowerPoint formats
function generateHTML(calculator, analysis) {
const { totalDebt, orthogonality, diagonalHealth, worstDrifts, blockDebts, diagonalDebt, offDiagonalDebt,
asymmetryDebt, upperTriangleDebt, lowerTriangleDebt, asymmetryRatio } = analysis;
// Generate calculation signature
const crypto = require('crypto');
const calculationData = JSON.stringify({
totalDebt,
orthogonality,
timestamp: new Date().toISOString()
});
const signature = crypto.createHash('sha256').update(calculationData).digest('hex').substring(0, 8);
// Get color for category with parent inheritance
function getCategoryColor(cat) {
// Find the root parent
let rootId = cat.id.substring(0, 2); // A📚 -> A
const parent = calculator.categories.find(c => c.id.startsWith(rootId) && c.depth === 0);
if (parent && parent.color) {
// Apply opacity based on depth
const opacity = 1.0 - (cat.depth * 0.2);
const hex = parent.color;
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
}
return '#888';
}
// Define parent categories and colors for dynamic border generation
const parentCategories = calculator.categories.filter(cat => cat.depth === 0);
const parentColors = ['#ff6600', '#9900ff', '#00ffff', '#ffff00', '#ff0099'];
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Trust Debt Analysis - ShortLex Final</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'SF Mono', Monaco, monospace;
background: #0a0a0a;
color: #fff;
padding: 20px;
}
.container { max-width: 1800px; margin: 0 auto; }
h1 {
text-align: center;
font-size: 2.5em;
margin-bottom: 10px;
background: linear-gradient(90deg, #00ff88, #00aaff, #ffaa00, #ff00aa, #ff0044);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle {
text-align: center;
color: #666;
margin-bottom: 30px;
}
/* Stats Grid */
.stats {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 15px;
margin-bottom: 30px;
}
.stat {
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
padding: 15px;
text-align: center;
border: 2px solid;
}
.stat:nth-child(1) { border-color: #00ff88; }
.stat:nth-child(2) { border-color: #00aaff; }
.stat:nth-child(3) { border-color: #ffaa00; }
.stat:nth-child(4) { border-color: #ff00aa; }
.stat:nth-child(5) { border-color: #ff0044; }
.stat-value {
font-size: 1.8em;
font-weight: bold;
margin: 5px 0;
}
.stat-label {
color: #888;
font-size: 0.9em;
}
/* PDF Export Button */
.pdf-button {
position: fixed;
top: 20px;
right: 20px;
background: linear-gradient(45deg, #00ff88, #00aaff);
color: #000;
border: none;
padding: 12px 24px;
border-radius: 25px;
font-weight: bold;
cursor: pointer;
z-index: 1000;
transition: all 0.3s;
}
.pdf-button:hover {
transform: scale(1.05);
box-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
}
/* Print-specific styles for PDF */
@media print {
@page {
size: landscape;
margin: 10mm;
}
body {
background: #1a1a1a !important;
color: #ccc !important;
print-color-adjust: exact !important;
-webkit-print-color-adjust: exact !important;
}
.container {
max-width: 100% !important;
padding: 10px !important;
}
.pdf-button {
display: none !important;
}
h1 {
background: linear-gradient(90deg, #00ff88, #00aaff) !important;
-webkit-background-clip: text !important;
-webkit-text-fill-color: transparent !important;
print-color-adjust: exact !important;
-webkit-print-color-adjust: exact !important;
}
.stat {
background: rgba(255, 255, 255, 0.05) !important;
color: #ccc !important;
print-color-adjust: exact !important;
-webkit-print-color-adjust: exact !important;
}
.block {
background: rgba(255, 255, 255, 0.02) !important;
print-color-adjust: exact !important;
-webkit-print-color-adjust: exact !important;
}
/* Matrix table fits on page */
.matrix-container {
overflow: visible !important;
transform: scale(0.7) !important;
transform-origin: top left !important;
margin-bottom: -30% !important;
}
table {
background: #2a2a2a !important;
border: 1px solid #444 !important;
width: 100% !important;
font-size: 9px !important;
}
th, td {
border: 1px solid #333 !important;
background: #1a1a1a !important;
color: #ccc !important;
padding: 2px !important;
font-size: 8px !important;
}
th {
background: #2a2a2a !important;
color: #888 !important;
}
.debt-critical { color: #d00 !important; }
.debt-high { color: #f60 !important; }
.debt-medium { color: #fa0 !important; }
.debt-low { color: #666 !important; }
.debt-undoc-high { color: #00d !important; }
.debt-undoc-medium { color: #0af !important; }
.debt-undoc-low { color: #0ff !important; }
.diagonal {
background: #fffacd !important;
print-color-adjust: exact !important;
-webkit-print-color-adjust: exact !important;
}
/* Ensure borders print */
* {
print-color-adjust: exact !important;
-webkit-print-color-adjust: exact !important;
}
}
/* Block Debts */
.blocks {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
margin-bottom: 30px;
}
.block {
background: rgba(255, 255, 255, 0.02);
border-radius: 8px;
padding: 15px;
text-align: center;
}
/* Matrix */
.matrix-container {
background: rgba(255, 255, 255, 0.02);
border-radius: 12px;
padding: 20px;
overflow-x: auto;
margin-bottom: 30px;
}
table {
width: 100%;
border-collapse: separate;
border-spacing: 1px;
font-size: 10px;
}
th, td {
padding: 4px 2px;
text-align: center;
position: relative;
min-width: 35px;
height: 35px;
}
th {
background: rgba(255, 255, 255, 0.1);
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Row headers (vertical axis) - full labels */
th.row-header {
text-align: left;
min-width: 180px;
padding-left: 5px;
padding-right: 5px;
font-size: 11px;
}
th.row-header .full-id {
font-weight: bold;
margin-right: 5px;
}
th.row-header .name {
opacity: 0.8;
font-weight: normal;
}
/* Header colors by depth and parent */
.depth-0 { font-weight: bold; font-size: 11px; }
.depth-1 { padding-left: 8px; font-size: 10px; opacity: 0.9; }
.depth-2 { padding-left: 16px; font-size: 9px; opacity: 0.8; }
/* Block boundaries - double walls with both colors */
/* Each block has its own color on its side */
/* DYNAMIC BORDERS: Generated based on actual parent categories */
${parentCategories.map((parent, index) => {
const parentLetter = parent.id.charAt(0);
const parentColor = parentColors[index] || '#888';
return `
/* ${parent.id} ${parent.name} borders */
.block-end-${parentLetter} {
border-right: 3px solid ${parentColor} !important;
padding-right: 3px !important;
}
.block-start-${parentLetter} {
border-left: 3px solid ${parentColor} !important;
padding-left: 3px !important;
}
.block-end-row-${parentLetter} {
border-bottom: 3px solid ${parentColor} !important;
padding-bottom: 3px !important;
}
.block-start-row-${parentLetter} {
border-top: 3px solid ${parentColor} !important;
padding-top: 3px !important;
}`;
}).join('')}
td {
background: rgba(0, 0, 0, 0.3);
transition: all 0.2s;
border: 1px solid rgba(255, 255, 255, 0.05);
}
td.diagonal {
background: repeating-linear-gradient(
45deg,
transparent,
transparent 5px,
rgba(255, 255, 255, 0.05) 5px,
rgba(255, 255, 255, 0.05) 10px
);
}
/* Heat coloring - bright colors for high amplitude */
.debt-none {
color: #333;
opacity: 0.3;
}
.debt-low {
color: #777;
opacity: 0.5;
}
.debt-medium {
color: #ffcc00;
font-weight: 500;
}
.debt-high {
color: #ff6600;
font-weight: 600;
text-shadow: 0 0 1px rgba(255, 102, 0, 0.3);
}
.debt-critical {
color: #ff0044;
font-weight: bold;
text-shadow: 0 0 3px rgba(255, 0, 68, 0.5);
}
td:hover {
backgroun