| <!DOCTYPE html>
|
| <html lang="en">
|
| <head>
|
| <meta charset="UTF-8">
|
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| <title>LTMS Truth Maintenance — Live Console</title>
|
| <style>
|
| :root {
|
| --bg-void: #020508;
|
| --bg-deep: #050b12;
|
| --bg-panel: rgba(8, 18, 28, 0.82);
|
| --bg-panel-strong: rgba(9, 24, 36, 0.96);
|
|
|
| --line-soft: rgba(112, 220, 255, 0.12);
|
| --line-medium: rgba(112, 220, 255, 0.28);
|
| --line-bright: rgba(112, 220, 255, 0.72);
|
|
|
| --text-primary: #e8fbff;
|
| --text-secondary: #8eb5c1;
|
| --text-muted: #52707a;
|
|
|
| --cyan: #56e8ff;
|
| --cyan-hot: #a9f7ff;
|
| --teal: #38ffc7;
|
| --violet: #ab7cff;
|
| --gold: #ffc857;
|
| --red: #ff5470;
|
| --green: #5dff9d;
|
|
|
| --shadow-cyan: 0 0 18px rgba(86, 232, 255, 0.2), 0 0 52px rgba(86, 232, 255, 0.08);
|
| --shadow-violet: 0 0 20px rgba(171, 124, 255, 0.2), 0 0 60px rgba(171, 124, 255, 0.08);
|
|
|
| --radius-xs: 6px;
|
| --radius-sm: 10px;
|
| --radius-md: 16px;
|
| --radius-lg: 24px;
|
|
|
| --font-display: "Orbitron", "Rajdhani", "Segoe UI", sans-serif;
|
| --font-mono: "JetBrains Mono", "IBM Plex Mono", "Cascadia Code", monospace;
|
| --ease-system: cubic-bezier(0.22, 1, 0.36, 1);
|
| }
|
|
|
| * { box-sizing: border-box; }
|
| html { min-height: 100%; background: var(--bg-void); }
|
| body {
|
| min-height: 100vh;
|
| margin: 0;
|
| color: var(--text-primary);
|
| font-family: var(--font-mono);
|
| background: linear-gradient(180deg, #020508 0%, #041019 48%, #020609 100%);
|
| }
|
|
|
| button, input, textarea { font: inherit; color: inherit; }
|
|
|
| .ltms-shell {
|
| width: min(1800px, 100%);
|
| margin: 0 auto;
|
| padding: 18px;
|
| }
|
|
|
| .ltms-header {
|
| position: sticky;
|
| top: 12px;
|
| z-index: 30;
|
| display: flex;
|
| gap: 20px;
|
| align-items: center;
|
| min-height: 76px;
|
| padding: 14px 18px;
|
| border: 1px solid var(--line-medium);
|
| border-radius: var(--radius-lg);
|
| background: linear-gradient(135deg, rgba(9, 24, 36, 0.94), rgba(4, 12, 19, 0.91));
|
| box-shadow: var(--shadow-cyan), inset 0 1px rgba(255, 255, 255, 0.04);
|
| backdrop-filter: blur(22px) saturate(135%);
|
| margin-bottom: 20px;
|
| }
|
|
|
| .ltms-title {
|
| font-family: var(--font-display);
|
| font-size: 1.35rem;
|
| font-weight: 700;
|
| letter-spacing: 0.15em;
|
| text-transform: uppercase;
|
| color: var(--violet);
|
| }
|
|
|
| .ltms-subtitle {
|
| color: var(--text-secondary);
|
| font-size: 0.75rem;
|
| letter-spacing: 0.1em;
|
| }
|
|
|
| .ltms-status {
|
| display: flex;
|
| gap: 12px;
|
| margin-left: auto;
|
| }
|
|
|
| .status-chip {
|
| display: inline-flex;
|
| gap: 8px;
|
| align-items: center;
|
| padding: 8px 12px;
|
| border: 1px solid var(--line-soft);
|
| border-radius: 999px;
|
| color: var(--text-secondary);
|
| background: rgba(2, 10, 16, 0.55);
|
| font-size: 0.75rem;
|
| }
|
|
|
| .status-chip[data-state="ready"] {
|
| color: var(--green);
|
| border-color: rgba(93, 255, 157, 0.35);
|
| }
|
|
|
| .status-dot {
|
| width: 6px;
|
| height: 6px;
|
| border-radius: 50%;
|
| background: currentColor;
|
| box-shadow: 0 0 10px currentColor;
|
| }
|
|
|
| .ltms-grid {
|
| display: grid;
|
| grid-template-columns: 1fr 1fr;
|
| gap: 16px;
|
| margin-bottom: 20px;
|
| }
|
|
|
| .ltms-panel {
|
| border: 1px solid var(--line-soft);
|
| border-radius: var(--radius-md);
|
| background: linear-gradient(160deg, rgba(10, 26, 38, 0.9), rgba(3, 11, 17, 0.96));
|
| box-shadow: 0 20px 80px rgba(0, 0, 0, 0.26), inset 0 1px rgba(255, 255, 255, 0.035);
|
| overflow: hidden;
|
| }
|
|
|
| .panel-header {
|
| display: flex;
|
| gap: 12px;
|
| align-items: center;
|
| min-height: 52px;
|
| padding: 0 16px;
|
| border-bottom: 1px solid var(--line-soft);
|
| background: linear-gradient(180deg, rgba(255, 255, 255, 0.025), transparent);
|
| }
|
|
|
| .panel-title {
|
| display: flex;
|
| gap: 10px;
|
| align-items: center;
|
| margin: 0;
|
| color: var(--cyan-hot);
|
| font-family: var(--font-display);
|
| font-size: 0.78rem;
|
| letter-spacing: 0.14em;
|
| text-transform: uppercase;
|
| }
|
|
|
| .panel-body {
|
| padding: 14px;
|
| }
|
|
|
| .input-group {
|
| display: flex;
|
| gap: 8px;
|
| margin-bottom: 12px;
|
| }
|
|
|
| .input-group input {
|
| flex: 1;
|
| padding: 8px 12px;
|
| border: 1px solid var(--line-soft);
|
| background: #03080d;
|
| color: var(--cyan);
|
| border-radius: var(--radius-xs);
|
| font-size: 0.85rem;
|
| }
|
|
|
| .input-group button {
|
| padding: 8px 16px;
|
| border: 1px solid var(--line-medium);
|
| background: linear-gradient(180deg, rgba(18, 47, 65, 0.9), rgba(6, 19, 28, 0.95));
|
| color: var(--text-primary);
|
| border-radius: var(--radius-xs);
|
| cursor: pointer;
|
| transition: all 160ms var(--ease-system);
|
| text-transform: uppercase;
|
| font-size: 0.75rem;
|
| font-weight: bold;
|
| }
|
|
|
| .input-group button:hover {
|
| border-color: var(--line-bright);
|
| box-shadow: var(--shadow-cyan);
|
| }
|
|
|
| .input-group button[data-variant="primary"] {
|
| background: linear-gradient(135deg, var(--cyan-hot), var(--cyan));
|
| color: #001115;
|
| border-color: transparent;
|
| }
|
|
|
| .belief-list {
|
| display: flex;
|
| flex-direction: column;
|
| gap: 8px;
|
| max-height: 500px;
|
| overflow-y: auto;
|
| }
|
|
|
| .belief-node {
|
| position: relative;
|
| padding: 12px 14px 12px 18px;
|
| border: 1px solid var(--line-soft);
|
| border-radius: var(--radius-sm);
|
| background: rgba(4, 13, 20, 0.78);
|
| transition: all 200ms var(--ease-system);
|
| }
|
|
|
| .belief-node::before {
|
| position: absolute;
|
| top: 14px;
|
| bottom: 14px;
|
| left: 8px;
|
| width: 2px;
|
| content: "";
|
| border-radius: 2px;
|
| background: linear-gradient(var(--cyan), var(--violet));
|
| }
|
|
|
| .belief-node[data-status="asserted"] {
|
| border-left-color: var(--green);
|
| }
|
|
|
| .belief-node[data-status="assumed"] {
|
| border-left-color: var(--gold);
|
| }
|
|
|
| .belief-node[data-status="derived"] {
|
| border-left-color: var(--teal);
|
| }
|
|
|
| .belief-node[data-status="retracted"] {
|
| border-left-color: var(--red);
|
| opacity: 0.5;
|
| text-decoration: line-through;
|
| }
|
|
|
| .belief-label {
|
| color: var(--text-primary);
|
| font-weight: bold;
|
| margin-bottom: 4px;
|
| }
|
|
|
| .belief-meta {
|
| display: flex;
|
| gap: 12px;
|
| font-size: 0.7rem;
|
| color: var(--text-muted);
|
| margin-top: 4px;
|
| }
|
|
|
| .belief-action {
|
| font-size: 0.65rem;
|
| color: var(--cyan);
|
| cursor: pointer;
|
| padding: 2px 6px;
|
| border-radius: 3px;
|
| transition: all 100ms var(--ease-system);
|
| }
|
|
|
| .belief-action:hover {
|
| background: rgba(86, 232, 255, 0.1);
|
| }
|
|
|
| .derivation-chain {
|
| display: flex;
|
| flex-direction: column;
|
| gap: 8px;
|
| max-height: 500px;
|
| overflow-y: auto;
|
| }
|
|
|
| .derivation-step {
|
| padding: 10px;
|
| border-left: 3px solid var(--teal);
|
| background: rgba(56, 255, 199, 0.05);
|
| border-radius: var(--radius-xs);
|
| font-size: 0.8rem;
|
| }
|
|
|
| .derivation-step .step-label {
|
| color: var(--teal);
|
| font-weight: bold;
|
| margin-bottom: 4px;
|
| }
|
|
|
| .derivation-step .step-content {
|
| color: var(--text-secondary);
|
| font-family: var(--font-mono);
|
| }
|
|
|
| .contradiction-alert {
|
| padding: 12px;
|
| border: 1px solid var(--red);
|
| background: rgba(255, 84, 112, 0.1);
|
| border-radius: var(--radius-sm);
|
| color: var(--red);
|
| margin-bottom: 12px;
|
| animation: pulse-red 1s infinite;
|
| }
|
|
|
| @keyframes pulse-red {
|
| 0%, 100% { box-shadow: inset 0 0 0 1px rgba(255, 84, 112, 0.3); }
|
| 50% { box-shadow: inset 0 0 0 1px rgba(255, 84, 112, 0.8); }
|
| }
|
|
|
| .execution-trace {
|
| display: flex;
|
| flex-direction: column;
|
| gap: 6px;
|
| max-height: 600px;
|
| overflow-y: auto;
|
| font-size: 0.75rem;
|
| }
|
|
|
| .trace-line {
|
| padding: 6px 8px;
|
| border-left: 2px solid var(--line-soft);
|
| color: var(--text-secondary);
|
| }
|
|
|
| .trace-line.assert {
|
| border-left-color: var(--green);
|
| color: var(--green);
|
| }
|
|
|
| .trace-line.assume {
|
| border-left-color: var(--gold);
|
| color: var(--gold);
|
| }
|
|
|
| .trace-line.derive {
|
| border-left-color: var(--teal);
|
| color: var(--teal);
|
| }
|
|
|
| .trace-line.retract {
|
| border-left-color: var(--red);
|
| color: var(--red);
|
| }
|
|
|
| .demo-buttons {
|
| display: grid;
|
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
| gap: 8px;
|
| margin-top: 16px;
|
| padding-top: 16px;
|
| border-top: 1px solid var(--line-soft);
|
| }
|
|
|
| .demo-btn {
|
| padding: 10px 12px;
|
| border: 1px solid var(--line-soft);
|
| background: rgba(2, 10, 16, 0.55);
|
| color: var(--cyan);
|
| border-radius: var(--radius-xs);
|
| cursor: pointer;
|
| font-size: 0.75rem;
|
| transition: all 100ms var(--ease-system);
|
| }
|
|
|
| .demo-btn:hover {
|
| border-color: var(--line-bright);
|
| background: rgba(86, 232, 255, 0.1);
|
| }
|
|
|
| .full-width {
|
| grid-column: 1 / -1;
|
| }
|
|
|
| @media (max-width: 1024px) {
|
| .ltms-grid {
|
| grid-template-columns: 1fr;
|
| }
|
| .ltms-header {
|
| flex-direction: column;
|
| align-items: flex-start;
|
| }
|
| .ltms-status {
|
| margin-left: 0;
|
| width: 100%;
|
| }
|
| }
|
| </style>
|
| </head>
|
| <body>
|
| <div class="ltms-shell">
|
| <div class="ltms-header">
|
| <div>
|
| <h1 class="ltms-title">🧠 LTMS</h1>
|
| <p class="ltms-subtitle">Layered Truth Maintenance System — Live Belief Graph</p>
|
| </div>
|
| <div class="ltms-status">
|
| <div class="status-chip" data-state="ready">
|
| <div class="status-dot"></div>
|
| <span>KNOWLEDGE ENGINE</span>
|
| </div>
|
| </div>
|
| </div>
|
|
|
| <div class="ltms-grid">
|
|
|
| <div class="ltms-panel">
|
| <div class="panel-header">
|
| <h2 class="panel-title">📝 Operations</h2>
|
| </div>
|
| <div class="panel-body">
|
| <div class="input-group">
|
| <input type="text" id="belief-input" placeholder="Enter belief (e.g., Socrates is human)">
|
| <button onclick="assertBelief()" data-variant="primary">Assert</button>
|
| </div>
|
|
|
| <div class="input-group">
|
| <input type="text" id="rule-input" placeholder="Enter rule (e.g., Every human is mortal)">
|
| <button onclick="addRule()">Rule</button>
|
| </div>
|
|
|
| <div style="margin-top: 16px; border-top: 1px solid var(--line-soft); padding-top: 16px;">
|
| <h3 style="color: var(--gold); font-size: 0.8rem; margin: 0 0 12px; text-transform: uppercase;">Queries</h3>
|
| <div class="input-group">
|
| <input type="text" id="query-input" placeholder="Query belief (e.g., Socrates is mortal)">
|
| <button onclick="queryBelief()">Exact</button>
|
| </div>
|
| <div class="input-group">
|
| <input type="text" id="semantic-input" placeholder="Semantic search (e.g., mortality)">
|
| <button onclick="semanticSearch()">🔍 Semantic</button>
|
| </div>
|
| </div>
|
|
|
| <div class="demo-buttons">
|
| <button class="demo-btn" onclick="socraticDemo()">📖 Socratic Demo</button>
|
| <button class="demo-btn" onclick="contradictionDemo()">⚠️ Contradiction</button>
|
| <button class="demo-btn" onclick="clearAll()">🔄 Clear All</button>
|
| </div>
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="ltms-panel">
|
| <div class="panel-header">
|
| <h2 class="panel-title">💭 Beliefs</h2>
|
| <span style="margin-left: auto; color: var(--text-muted); font-size: 0.7rem;" id="belief-count">0 active</span>
|
| </div>
|
| <div class="panel-body">
|
| <div id="contradiction-alerts"></div>
|
| <div class="belief-list" id="belief-list">
|
| <div style="color: var(--text-muted); font-size: 0.75rem;">No beliefs yet. Assert or assume a belief to begin.</div>
|
| </div>
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="ltms-panel full-width">
|
| <div class="panel-header">
|
| <h2 class="panel-title">⛓️ Derivation Chain</h2>
|
| <span style="margin-left: auto; color: var(--text-muted); font-size: 0.7rem;" id="step-count">0 steps</span>
|
| </div>
|
| <div class="panel-body">
|
| <div class="derivation-chain" id="derivation-chain">
|
| <div style="color: var(--text-muted); font-size: 0.75rem;">Waiting for inferences...</div>
|
| </div>
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="ltms-panel full-width">
|
| <div class="panel-header">
|
| <h2 class="panel-title">📋 Execution Log</h2>
|
| </div>
|
| <div class="panel-body">
|
| <div class="execution-trace" id="execution-trace">
|
| <div class="trace-line" style="color: var(--cyan);">• System initialized</div>
|
| </div>
|
| </div>
|
| </div>
|
| </div>
|
| </div>
|
|
|
| <script type="module">
|
| import ONNXBridge from './onnx-bridge.mjs';
|
| window.ONNXBridge = ONNXBridge;
|
| </script>
|
|
|
| <script>
|
|
|
|
|
|
|
|
|
| const state = {
|
| beliefs: new Map(),
|
| rules: [],
|
| derivations: [],
|
| contradictions: [],
|
| executionLog: [],
|
| beliefCounter: 0,
|
| };
|
|
|
| const STATUS = {
|
| ASSERTED: 'asserted',
|
| ASSUMED: 'assumed',
|
| DERIVED: 'derived',
|
| RETRACTED: 'retracted',
|
| };
|
|
|
|
|
|
|
|
|
|
|
| function assertBelief() {
|
| const input = document.getElementById('belief-input');
|
| const label = input.value.trim();
|
|
|
| if (!label) return;
|
|
|
| const id = `belief-${++state.beliefCounter}`;
|
| state.beliefs.set(id, {
|
| id,
|
| label,
|
| status: STATUS.ASSERTED,
|
| justification: 'user assertion',
|
| timestamp: Date.now(),
|
| dependents: [],
|
| });
|
|
|
| logExecution('assert', label);
|
| input.value = '';
|
|
|
|
|
| checkContradictions();
|
|
|
|
|
| inferenceStep();
|
|
|
| render();
|
| }
|
|
|
| function addRule() {
|
| const input = document.getElementById('rule-input');
|
| const rule = input.value.trim();
|
|
|
| if (!rule) return;
|
|
|
|
|
| const match = rule.match(/If\s+(.+?)\s+then\s+(.+)/i);
|
| if (!match) {
|
| logExecution('error', 'Rule format: "If X then Y"');
|
| return;
|
| }
|
|
|
| state.rules.push({
|
| antecedent: match[1].trim(),
|
| consequent: match[2].trim(),
|
| timestamp: Date.now(),
|
| });
|
|
|
| logExecution('rule_add', rule);
|
| input.value = '';
|
|
|
|
|
| inferenceStep();
|
|
|
| render();
|
| }
|
|
|
| function queryBelief() {
|
| const input = document.getElementById('query-input');
|
| const query = input.value.trim();
|
|
|
| if (!query) return;
|
|
|
| const matches = Array.from(state.beliefs.values())
|
| .filter(b => b.label.toLowerCase().includes(query.toLowerCase()) && b.status !== STATUS.RETRACTED);
|
|
|
| if (matches.length === 0) {
|
| logExecution('query', `No beliefs match: "${query}"`);
|
| } else {
|
| matches.forEach(m => {
|
| logExecution('query_result', `${m.label} (${m.status})`);
|
| });
|
| }
|
|
|
| input.value = '';
|
| render();
|
| }
|
|
|
| async function semanticSearch() {
|
| const input = document.getElementById('semantic-input');
|
| const query = input.value.trim();
|
|
|
| if (!query) return;
|
|
|
| logExecution('semantic_search', `Searching for: "${query}"`);
|
|
|
| try {
|
| if (!window.ONNXBridge) {
|
| logExecution('error', 'ONNX not loaded');
|
| return;
|
| }
|
|
|
|
|
| const candidates = Array.from(state.beliefs.values())
|
| .filter(b => b.status !== STATUS.RETRACTED)
|
| .map(b => b.label);
|
|
|
| if (candidates.length === 0) {
|
| logExecution('semantic_result', 'No beliefs to search');
|
| input.value = '';
|
| return;
|
| }
|
|
|
|
|
| const results = await window.ONNXBridge.search(query, candidates);
|
|
|
| if (results.length === 0) {
|
| logExecution('semantic_result', 'No semantically similar beliefs found');
|
| } else {
|
| results.slice(0, 5).forEach(r => {
|
| logExecution('semantic_result', `"${r.text}" (similarity: ${(r.similarity * 100).toFixed(0)}%)`);
|
| });
|
| }
|
| } catch (err) {
|
| logExecution('error', `Semantic search failed: ${err.message}`);
|
| }
|
|
|
| input.value = '';
|
| render();
|
| }
|
|
|
| function inferenceStep() {
|
| state.rules.forEach(rule => {
|
|
|
| const antecedentBeliefs = Array.from(state.beliefs.values())
|
| .filter(b => b.label.toLowerCase().includes(rule.antecedent.toLowerCase()) && b.status !== STATUS.RETRACTED);
|
|
|
| if (antecedentBeliefs.length > 0) {
|
|
|
| const consequentId = `belief-${++state.beliefCounter}`;
|
| const consequent = rule.consequent.replace('{subject}', 'it');
|
|
|
| state.beliefs.set(consequentId, {
|
| id: consequentId,
|
| label: consequent,
|
| status: STATUS.DERIVED,
|
| justification: `From: ${rule.antecedent}`,
|
| timestamp: Date.now(),
|
| dependents: [],
|
| });
|
|
|
| state.derivations.push({
|
| step: state.derivations.length + 1,
|
| antecedent: antecedentBeliefs[0].label,
|
| rule: rule.antecedent + ' → ' + rule.consequent,
|
| consequent,
|
| timestamp: Date.now(),
|
| });
|
|
|
| logExecution('derive', consequent);
|
| }
|
| });
|
| }
|
|
|
| function checkContradictions() {
|
| const active = Array.from(state.beliefs.values())
|
| .filter(b => b.status !== STATUS.RETRACTED);
|
|
|
| state.contradictions = [];
|
|
|
| for (let i = 0; i < active.length; i++) {
|
| for (let j = i + 1; j < active.length; j++) {
|
| const b1 = active[i];
|
| const b2 = active[j];
|
|
|
|
|
| const negPattern1 = `${b1.label.split(' is ')[0]} is not ${b1.label.split(' is ')[1]}`;
|
| const negPattern2 = `${b2.label.split(' is ')[0]} is not ${b2.label.split(' is ')[1]}`;
|
|
|
| if (b1.label === negPattern2 || b2.label === negPattern1) {
|
| state.contradictions.push({ belief1: b1, belief2: b2 });
|
| }
|
| }
|
| }
|
| }
|
|
|
| function retractBelief(beliefId) {
|
| const belief = state.beliefs.get(beliefId);
|
| if (!belief) return;
|
|
|
|
|
| belief.status = STATUS.RETRACTED;
|
|
|
|
|
| state.derivations.forEach(d => {
|
| if (d.antecedent === belief.label) {
|
| const dependentBeliefs = Array.from(state.beliefs.values())
|
| .filter(b => b.label === d.consequent);
|
|
|
| dependentBeliefs.forEach(b => {
|
| b.status = STATUS.RETRACTED;
|
| logExecution('cascade_retract', b.label);
|
| });
|
| }
|
| });
|
|
|
| logExecution('retract', belief.label);
|
| render();
|
| }
|
|
|
|
|
|
|
|
|
|
|
| function socraticDemo() {
|
| clearAll();
|
|
|
| setTimeout(() => {
|
| document.getElementById('belief-input').value = 'Socrates is human';
|
| assertBelief();
|
| }, 200);
|
|
|
| setTimeout(() => {
|
| document.getElementById('rule-input').value = 'If Socrates is human then Socrates is mortal';
|
| addRule();
|
| }, 600);
|
|
|
| setTimeout(() => {
|
| document.getElementById('query-input').value = 'mortal';
|
| queryBelief();
|
| }, 1200);
|
|
|
| setTimeout(() => {
|
| logExecution('demo_info', 'Now retracting: "Socrates is human"');
|
| }, 1600);
|
|
|
| setTimeout(() => {
|
| const beliefId = Array.from(state.beliefs.entries())
|
| .find(([_, b]) => b.label === 'Socrates is human')?.[0];
|
| if (beliefId) retractBelief(beliefId);
|
| }, 2200);
|
| }
|
|
|
| function contradictionDemo() {
|
| clearAll();
|
|
|
| setTimeout(() => {
|
| document.getElementById('belief-input').value = 'The light is on';
|
| assertBelief();
|
| }, 200);
|
|
|
| setTimeout(() => {
|
| document.getElementById('belief-input').value = 'The light is not on';
|
| assertBelief();
|
| }, 600);
|
| }
|
|
|
| function clearAll() {
|
| state.beliefs.clear();
|
| state.rules = [];
|
| state.derivations = [];
|
| state.contradictions = [];
|
| state.executionLog = [];
|
| state.beliefCounter = 0;
|
| logExecution('system', 'Cleared all state');
|
| render();
|
| }
|
|
|
|
|
|
|
|
|
|
|
| function logExecution(action, detail) {
|
| state.executionLog.push({
|
| timestamp: new Date().toLocaleTimeString(),
|
| action,
|
| detail,
|
| });
|
| }
|
|
|
|
|
|
|
|
|
|
|
| function render() {
|
| renderBeliefs();
|
| renderDerivations();
|
| renderExecutionLog();
|
| renderContradictions();
|
| }
|
|
|
| function renderBeliefs() {
|
| const list = document.getElementById('belief-list');
|
| const beliefs = Array.from(state.beliefs.values());
|
| const count = beliefs.filter(b => b.status !== STATUS.RETRACTED).length;
|
|
|
| document.getElementById('belief-count').textContent = `${count} active`;
|
|
|
| if (beliefs.length === 0) {
|
| list.innerHTML = '<div style="color: var(--text-muted); font-size: 0.75rem;">No beliefs yet.</div>';
|
| return;
|
| }
|
|
|
| list.innerHTML = beliefs
|
| .map(b => `
|
| <div class="belief-node" data-status="${b.status}">
|
| <div class="belief-label">${b.label}</div>
|
| <div class="belief-meta">
|
| <span>${b.status}</span>
|
| <span>${b.justification}</span>
|
| <span class="belief-action" onclick="retractBelief('${b.id}')">retract</span>
|
| </div>
|
| </div>
|
| `)
|
| .join('');
|
| }
|
|
|
| function renderDerivations() {
|
| const chain = document.getElementById('derivation-chain');
|
| const derivations = state.derivations;
|
|
|
| document.getElementById('step-count').textContent = `${derivations.length} steps`;
|
|
|
| if (derivations.length === 0) {
|
| chain.innerHTML = '<div style="color: var(--text-muted); font-size: 0.75rem;">Waiting for inferences...</div>';
|
| return;
|
| }
|
|
|
| chain.innerHTML = derivations
|
| .map((d, i) => `
|
| <div class="derivation-step">
|
| <div class="step-label">Step ${d.step}:</div>
|
| <div class="step-content">
|
| <strong>${d.antecedent}</strong><br>
|
| + ${d.rule}<br>
|
| ⇒ <strong>${d.consequent}</strong>
|
| </div>
|
| </div>
|
| `)
|
| .join('');
|
| }
|
|
|
| function renderContradictions() {
|
| const alerts = document.getElementById('contradiction-alerts');
|
|
|
| if (state.contradictions.length === 0) {
|
| alerts.innerHTML = '';
|
| return;
|
| }
|
|
|
| alerts.innerHTML = state.contradictions
|
| .map(c => `
|
| <div class="contradiction-alert">
|
| ⚠️ Contradiction: "${c.belief1.label}" vs "${c.belief2.label}"
|
| </div>
|
| `)
|
| .join('');
|
| }
|
|
|
| function renderExecutionLog() {
|
| const trace = document.getElementById('execution-trace');
|
| const logs = state.executionLog.slice(-50);
|
|
|
| trace.innerHTML = logs
|
| .map(log => `
|
| <div class="trace-line ${log.action}">
|
| <strong>${log.timestamp}</strong> • ${log.action}: ${log.detail}
|
| </div>
|
| `)
|
| .join('');
|
|
|
| trace.scrollTop = trace.scrollHeight;
|
| }
|
|
|
|
|
|
|
|
|
|
|
| window.addEventListener('load', () => {
|
| logExecution('system', 'LTMS console initialized');
|
| render();
|
| });
|
|
|
| window.assertBelief = assertBelief;
|
| window.addRule = addRule;
|
| window.queryBelief = queryBelief;
|
| window.semanticSearch = semanticSearch;
|
| window.retractBelief = retractBelief;
|
| window.socraticDemo = socraticDemo;
|
| window.contradictionDemo = contradictionDemo;
|
| window.clearAll = clearAll;
|
|
|
|
|
| window.addEventListener('load', async () => {
|
| if (window.ONNXBridge) {
|
| const ready = await window.ONNXBridge.initialize();
|
| if (ready) {
|
| logExecution('onnx', 'Semantic search ready (ONNX in-browser)');
|
| } else {
|
| logExecution('onnx', 'Semantic search ready (fallback mode)');
|
| }
|
| }
|
| });
|
| </script>
|
| </body>
|
| </html>
|
|
|