Replace "ensures", "guarantee", "foolproof", "world-class" and similar absolute terms with evidence-based language throughout public pages, JS components, and FAQ content. Changes apply inst_017 (no absolute assurance terms) consistently. Replacements: - "ensures X" → "validates X", "so that X", "supports X", "maintains X" - "guarantee" → removed or rephrased with qualified language - "foolproof" → "infallible" - "architecturally impossible" → "architecture prevents without explicit override flags" Preserved: published research papers (architectural-alignment*.html), EU AI Act quotes, Te Tiriti treaty language, and FAQ meta-commentary that deliberately critiques this language (lines 2842-2896). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
428 lines
15 KiB
JavaScript
428 lines
15 KiB
JavaScript
/**
|
|
* Interactive Architecture Diagram Component
|
|
* Tractatus Framework - Phase 3: Interactive Architecture Diagram
|
|
*
|
|
* Handles click/hover interactions on the hexagonal service diagram
|
|
* Shows service details in a side panel
|
|
*/
|
|
|
|
class InteractiveDiagram {
|
|
constructor() {
|
|
this.activeService = null;
|
|
this.loadTranslations();
|
|
this.init();
|
|
}
|
|
|
|
loadTranslations() {
|
|
// Get translations from i18n system
|
|
const i18n = window.i18nTranslations || {};
|
|
const diagram = i18n.diagram_services || {};
|
|
|
|
// If no translations available, use English fallback
|
|
const hasTranslations = diagram && Object.keys(diagram).length > 0;
|
|
if (!hasTranslations) {
|
|
console.warn('[InteractiveDiagram] No translations loaded, using English fallback');
|
|
this.loadFallbackData();
|
|
return;
|
|
}
|
|
|
|
// Define static properties (icons and colors)
|
|
const staticProps = {
|
|
overview: { color: '#0ea5e9', icon: '⚙️' },
|
|
boundary: { color: '#10b981', icon: '🔒' },
|
|
instruction: { color: '#6366f1', icon: '📋' },
|
|
validator: { color: '#8b5cf6', icon: '✓' },
|
|
pressure: { color: '#f59e0b', icon: '⚡' },
|
|
metacognitive: { color: '#ec4899', icon: '💡' },
|
|
deliberation: { color: '#14b8a6', icon: '👥' }
|
|
};
|
|
|
|
// Build serviceData from translations
|
|
this.serviceData = {};
|
|
|
|
Object.keys(staticProps).forEach(serviceId => {
|
|
const trans = diagram[serviceId] || {};
|
|
const details = [];
|
|
|
|
// Collect detail1-detail6 (some services have 4, overview has 6)
|
|
for (let i = 1; i <= 6; i++) {
|
|
if (trans[`detail${i}`]) {
|
|
details.push(trans[`detail${i}`]);
|
|
}
|
|
}
|
|
|
|
this.serviceData[serviceId] = {
|
|
name: trans.name || serviceId,
|
|
shortName: trans.shortName || serviceId,
|
|
color: staticProps[serviceId].color,
|
|
icon: staticProps[serviceId].icon,
|
|
description: trans.description || '',
|
|
details: details,
|
|
promise: trans.promise || ''
|
|
};
|
|
});
|
|
|
|
console.log('[InteractiveDiagram] Loaded translations for', Object.keys(this.serviceData).length, 'services');
|
|
}
|
|
|
|
|
|
loadFallbackData() {
|
|
// English fallback data when i18n not ready
|
|
this.serviceData = {
|
|
overview: {
|
|
name: 'Tractatus Governance Layer',
|
|
shortName: 'Overview',
|
|
color: '#0ea5e9',
|
|
icon: '⚙️',
|
|
description: 'Six external governance services working together to enforce AI safety boundaries outside the AI runtime.',
|
|
details: [
|
|
'All services operate externally to the AI—making manipulation harder',
|
|
'Instruction storage and validation work together to prevent directive fade',
|
|
'Boundary enforcement and deliberation coordinate on values decisions',
|
|
'Pressure monitoring adjusts verification requirements dynamically',
|
|
'Metacognitive gates require AI to pause before high-risk operations',
|
|
'Each service addresses a different failure mode in AI safety'
|
|
],
|
|
promise: 'External architectural enforcement that is structurally more difficult to bypass than behavioral training alone.'
|
|
},
|
|
boundary: {
|
|
name: 'BoundaryEnforcer',
|
|
shortName: 'Boundary',
|
|
color: '#10b981',
|
|
icon: '🔒',
|
|
description: 'Blocks AI from making values decisions (privacy, ethics, strategic direction). Requires human approval.',
|
|
details: [
|
|
'Enforces Tractatus 12.1-12.7 boundaries',
|
|
'Values decisions architecturally require humans',
|
|
'Prevents AI autonomous decision-making on ethical questions',
|
|
'External enforcement - harder to bypass via prompting'
|
|
],
|
|
promise: 'Values boundaries enforced externally—harder to manipulate through prompting.'
|
|
},
|
|
instruction: {
|
|
name: 'InstructionPersistenceClassifier',
|
|
shortName: 'Instruction',
|
|
color: '#6366f1',
|
|
icon: '📋',
|
|
description: 'Stores instructions externally with persistence levels (HIGH/MEDIUM/LOW). Aims to reduce directive fade.',
|
|
details: [
|
|
'Quadrant-based classification (STR/OPS/TAC/SYS/STO)',
|
|
'Time-persistence metadata tagging',
|
|
'Temporal horizon modeling (STRATEGIC, OPERATIONAL, TACTICAL)',
|
|
'External storage independent of AI runtime'
|
|
],
|
|
promise: 'Instructions stored outside AI—more resistant to context manipulation.'
|
|
},
|
|
validator: {
|
|
name: 'CrossReferenceValidator',
|
|
shortName: 'Validator',
|
|
color: '#8b5cf6',
|
|
icon: '✓',
|
|
description: 'Validates AI actions against instruction history. Aims to prevent pattern bias overriding explicit directives.',
|
|
details: [
|
|
'Cross-references AI claims with external instruction history',
|
|
'Detects pattern-based overrides of explicit user directives',
|
|
'Independent verification layer',
|
|
'Helps prevent instruction drift'
|
|
],
|
|
promise: 'Independent verification—AI claims checked against external source.'
|
|
},
|
|
pressure: {
|
|
name: 'ContextPressureMonitor',
|
|
shortName: 'Pressure',
|
|
color: '#f59e0b',
|
|
icon: '⚡',
|
|
description: 'Monitors AI performance degradation. Escalates when context pressure threatens quality.',
|
|
details: [
|
|
'Tracks token usage, complexity, error rates',
|
|
'Detects degraded operating conditions',
|
|
'Adjusts verification requirements under pressure',
|
|
'Objective metrics for quality monitoring'
|
|
],
|
|
promise: 'Objective metrics may detect manipulation attempts early.'
|
|
},
|
|
metacognitive: {
|
|
name: 'MetacognitiveVerifier',
|
|
shortName: 'Metacognitive',
|
|
color: '#ec4899',
|
|
icon: '💡',
|
|
description: 'Requires AI to pause and verify complex operations before execution. Structural safety check.',
|
|
details: [
|
|
'AI self-checks alignment, coherence, safety before execution',
|
|
'Structural pause-and-verify gates',
|
|
'Selective verification (not constant)',
|
|
'Architectural enforcement of reflection steps'
|
|
],
|
|
promise: 'Architectural gates aim to enforce verification steps.'
|
|
},
|
|
deliberation: {
|
|
name: 'PluralisticDeliberationOrchestrator',
|
|
shortName: 'Deliberation',
|
|
color: '#14b8a6',
|
|
icon: '👥',
|
|
description: 'Facilitates multi-stakeholder deliberation for values conflicts where no single "correct" answer exists.',
|
|
details: [
|
|
'Non-hierarchical coordination for values conflicts',
|
|
'Stakeholder perspective representation',
|
|
'Consensus-building for ethical trade-offs',
|
|
'Addresses values pluralism in AI safety'
|
|
],
|
|
promise: 'Facilitates deliberation across stakeholder perspectives for values conflicts.'
|
|
}
|
|
};
|
|
console.log('[InteractiveDiagram] Loaded English fallback data for', Object.keys(this.serviceData).length, 'services');
|
|
}
|
|
|
|
handleLanguageChange() {
|
|
console.log('[InteractiveDiagram] Language changed, reloading translations');
|
|
this.loadTranslations();
|
|
// Re-render current service if one is active
|
|
if (this.activeService) {
|
|
const service = this.serviceData[this.activeService];
|
|
if (service) {
|
|
this.renderServicePanel(service);
|
|
}
|
|
}
|
|
}
|
|
|
|
init() {
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', () => this.setup());
|
|
} else {
|
|
this.setup();
|
|
}
|
|
|
|
console.log('[InteractiveDiagram] Initialized');
|
|
}
|
|
|
|
setup() {
|
|
// SVG is now inline in the HTML
|
|
const svg = document.getElementById('interactive-arch-diagram');
|
|
|
|
if (!svg) {
|
|
console.warn('[InteractiveDiagram] Inline SVG not found');
|
|
return;
|
|
}
|
|
|
|
console.log('[InteractiveDiagram] Found inline SVG');
|
|
this.svg = svg;
|
|
|
|
const nodes = svg.querySelectorAll('.service-node');
|
|
console.log(`[InteractiveDiagram] Found ${nodes.length} service nodes`);
|
|
|
|
if (nodes.length === 0) {
|
|
console.warn('[InteractiveDiagram] No service nodes found in SVG');
|
|
return;
|
|
}
|
|
|
|
nodes.forEach(node => {
|
|
const serviceId = node.getAttribute('data-service');
|
|
|
|
// Click handler
|
|
node.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.showServiceDetails(serviceId);
|
|
}, true);
|
|
|
|
// Touch support for mobile devices
|
|
node.addEventListener('touchstart', (e) => {
|
|
e.preventDefault();
|
|
const serviceId = node.getAttribute('data-service');
|
|
this.showServiceDetails(serviceId);
|
|
}, { passive: false });
|
|
|
|
// Hover effects
|
|
node.addEventListener('mouseenter', () => {
|
|
this.highlightService(serviceId);
|
|
});
|
|
|
|
node.addEventListener('mouseleave', () => {
|
|
this.unhighlightService(serviceId);
|
|
});
|
|
|
|
// Add pointer cursor via JavaScript (CSP-compliant)
|
|
node.style.cursor = 'pointer';
|
|
});
|
|
|
|
this.addKeyboardNavigation(nodes);
|
|
|
|
// Show initial state (overview)
|
|
this.showServiceDetails('overview');
|
|
console.log('[InteractiveDiagram] Setup complete, showing overview');
|
|
}
|
|
|
|
highlightService(serviceId) {
|
|
if (!this.svg) return;
|
|
|
|
const connectionLine = this.svg.querySelector(`#conn-${serviceId}`);
|
|
if (connectionLine) {
|
|
connectionLine.classList.add('active');
|
|
}
|
|
|
|
const node = this.svg.querySelector(`#node-${serviceId}`);
|
|
if (node) {
|
|
node.classList.add('hover');
|
|
}
|
|
}
|
|
|
|
unhighlightService(serviceId) {
|
|
if (!this.svg) return;
|
|
|
|
if (this.activeService === serviceId) return;
|
|
|
|
const connectionLine = this.svg.querySelector(`#conn-${serviceId}`);
|
|
if (connectionLine) {
|
|
connectionLine.classList.remove('active');
|
|
}
|
|
|
|
const node = this.svg.querySelector(`#node-${serviceId}`);
|
|
if (node) {
|
|
node.classList.remove('hover');
|
|
}
|
|
}
|
|
|
|
showServiceDetails(serviceId) {
|
|
const service = this.serviceData[serviceId];
|
|
if (!service) {
|
|
console.error('[InteractiveDiagram] Service not found:', serviceId);
|
|
return;
|
|
}
|
|
|
|
this.activeService = serviceId;
|
|
|
|
if (this.svg) {
|
|
this.svg.querySelectorAll('.service-node').forEach(n => n.classList.remove('active'));
|
|
this.svg.querySelectorAll('.connection-line').forEach(l => l.classList.remove('active'));
|
|
|
|
const node = this.svg.querySelector(`#node-${serviceId}`);
|
|
if (node) {
|
|
node.classList.add('active');
|
|
}
|
|
|
|
const connectionLine = this.svg.querySelector(`#conn-${serviceId}`);
|
|
if (connectionLine) {
|
|
connectionLine.classList.add('active');
|
|
}
|
|
}
|
|
|
|
this.renderServicePanel(service);
|
|
|
|
console.log('[InteractiveDiagram] Showing details for:', service.name);
|
|
}
|
|
|
|
renderServicePanel(service) {
|
|
const panel = document.getElementById('service-detail-panel');
|
|
|
|
if (!panel) {
|
|
console.error('[InteractiveDiagram] Service detail panel not found in DOM');
|
|
return;
|
|
}
|
|
|
|
// Update border color to match selected service
|
|
panel.style.borderColor = service.color;
|
|
panel.style.borderWidth = '2px';
|
|
|
|
const html = `
|
|
<div class="flex items-start mb-4">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-12 h-12 rounded-xl flex items-center justify-center text-2xl service-icon-box" data-color="${service.color}">
|
|
${service.icon}
|
|
</div>
|
|
<div>
|
|
<h3 class="text-xl font-bold text-gray-900">${service.name}</h3>
|
|
<span class="text-xs font-medium text-gray-600 uppercase">${service.shortName}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="text-gray-700 mb-4 leading-relaxed">${service.description}</p>
|
|
|
|
<div class="mb-4">
|
|
<h4 class="text-sm font-semibold text-gray-900 mb-2 uppercase">Key Features</h4>
|
|
<ul class="space-y-2" id="service-features-list">
|
|
${service.details.map(detail => `
|
|
<li class="flex items-start text-sm text-gray-700">
|
|
<svg class="w-4 h-4 mr-2 mt-0.5 flex-shrink-0 service-check-icon" data-color="${service.color}" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"/>
|
|
</svg>
|
|
<span>${detail}</span>
|
|
</li>
|
|
`).join('')}
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="text-xs rounded px-3 py-2 bg-opacity-20 service-promise-badge" data-color="${service.color}">
|
|
<strong class="service-promise-text" data-color="${service.color}">Early Promise:</strong>
|
|
<span class="text-gray-800">${service.promise}</span>
|
|
</div>
|
|
`;
|
|
|
|
panel.innerHTML = html;
|
|
|
|
// Apply styles via JavaScript (CSP-compliant)
|
|
const iconBox = panel.querySelector('.service-icon-box');
|
|
if (iconBox) {
|
|
const color = iconBox.getAttribute('data-color');
|
|
iconBox.style.background = `linear-gradient(135deg, ${color} 0%, ${color}dd 100%)`;
|
|
}
|
|
|
|
// Style all check icons
|
|
const checkIcons = panel.querySelectorAll('.service-check-icon');
|
|
checkIcons.forEach(icon => {
|
|
const color = icon.getAttribute('data-color');
|
|
icon.style.color = color;
|
|
});
|
|
|
|
// Style promise badge
|
|
const promiseBadge = panel.querySelector('.service-promise-badge');
|
|
if (promiseBadge) {
|
|
const color = promiseBadge.getAttribute('data-color');
|
|
promiseBadge.style.backgroundColor = color;
|
|
}
|
|
|
|
// Style promise text
|
|
const promiseText = panel.querySelector('.service-promise-text');
|
|
if (promiseText) {
|
|
const color = promiseText.getAttribute('data-color');
|
|
promiseText.style.color = color;
|
|
}
|
|
}
|
|
|
|
addKeyboardNavigation(nodes) {
|
|
nodes.forEach((node, index) => {
|
|
node.setAttribute('tabindex', '0');
|
|
node.setAttribute('role', 'button');
|
|
|
|
node.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault();
|
|
const serviceId = node.getAttribute('data-service');
|
|
this.showServiceDetails(serviceId);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
if (typeof window !== 'undefined') {
|
|
window.interactiveDiagram = new InteractiveDiagram();
|
|
|
|
// Listen for i18n initialization
|
|
window.addEventListener('i18nInitialized', () => {
|
|
if (window.interactiveDiagram) {
|
|
window.interactiveDiagram.handleLanguageChange();
|
|
}
|
|
});
|
|
|
|
// Listen for language changes
|
|
window.addEventListener('languageChanged', () => {
|
|
if (window.interactiveDiagram) {
|
|
window.interactiveDiagram.handleLanguageChange();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = InteractiveDiagram;
|
|
}
|