===== /home/yeff/public_html/devon/panel/assets/js/panel.js ===== 'use strict'; const BUCKETS = [ 'prerequisites', 'installation', 'configuration', 'validation', 'observable_evidence', 'failure_modes_recovery', 'completion_promotion' ]; const BUCKET_DESC = { prerequisites: 'Required conditions, blockers and hard dependencies', installation: 'File and artifact installation steps', configuration: 'Configuration and binding steps', validation: 'Validation gates and pass/fail criteria', observable_evidence: 'Filesystem and runtime observable evidence', failure_modes_recovery:'Known failure modes and recovery actions', completion_promotion: 'Done criteria and promotion gate' }; const G = { phases: [], cats: [], data: {}, openPhases: new Set(), openCats: new Set(), sel: { phaseId: null, catId: null, bucket: null } }; async function init() { try { const [hub, host, docker, runtime, progress] = await Promise.all([ fetch('data/hub_index.json').then(r => r.json()), fetch('data/host_runtime.json').then(r => r.json()).catch(() => null), fetch('data/docker_runtime.json').then(r => r.json()).catch(() => null), fetch('data/runtime_status.json').then(r => r.json()).catch(() => null), fetch('data/project_progress.json').then(r => r.json()).catch(() => null) ]); G.phases = hub.phases || []; G.cats = hub.categories || []; G.data = { host, docker, runtime, progress }; renderTree(); renderDetail(); renderFooter(); } catch (e) { document.getElementById('dp-tree').innerHTML = '
Load error: ' + e.message + '
'; } } function phaseOfCat(cat) { const m = (cat.badge || '').match(/\d+/); if (!m) return 'phase-01'; return 'phase-' + String(parseInt(m[0])).padStart(2, '0'); } function catsForPhase(phaseId) { return G.cats.filter(c => phaseOfCat(c) === phaseId); } /* ── TREE ─────────────────────────────────────────── */ function renderTree() { let html = ''; for (const ph of G.phases) { const isOpen = G.openPhases.has(ph.id); const phActive = G.sel.phaseId === ph.id ? ' ph-active' : ''; const num = ph.step.replace('Phase ', ''); html += `
${isOpen ? '▾' : '▸'} ${num} ${ph.name}
`; if (isOpen) { for (const cat of catsForPhase(ph.id)) { const isCatOpen = G.openCats.has(cat.id); const catActive = G.sel.catId === cat.id ? ' cat-active' : ''; html += `
${isCatOpen ? '▾' : '▸'} ${cat.title}
`; if (isCatOpen) { BUCKETS.forEach((bk, i) => { const bkActive = G.sel.catId === cat.id && G.sel.bucket === bk ? ' bk-active' : ''; html += `
${bk}
`; }); } } } } document.getElementById('dp-tree').innerHTML = html; bindTree(); } function bindTree() { document.querySelectorAll('.ph-row').forEach(el => { el.addEventListener('click', () => { const id = el.dataset.ph; if (G.openPhases.has(id)) { G.openPhases.delete(id); G.openCats.clear(); G.sel.catId = null; G.sel.bucket = null; } else { G.openPhases.add(id); } G.sel.phaseId = id; renderTree(); renderDetail(); }); }); document.querySelectorAll('.cat-row').forEach(el => { el.addEventListener('click', e => { e.stopPropagation(); const id = el.dataset.cat; if (G.openCats.has(id)) { G.openCats.delete(id); G.sel.bucket = null; } else { G.openCats.add(id); } G.sel.phaseId = el.dataset.ph; G.sel.catId = id; G.sel.bucket = null; renderTree(); renderDetail(); }); }); document.querySelectorAll('.bk-row').forEach(el => { el.addEventListener('click', e => { e.stopPropagation(); G.sel.phaseId = el.dataset.ph; G.sel.catId = el.dataset.cat; G.sel.bucket = el.dataset.bk; renderTree(); renderDetail(); }); }); } /* ── DETAIL ───────────────────────────────────────── */ function renderDetail() { const el = document.getElementById('dp-detail'); if (!G.sel.phaseId) { el.innerHTML = renderSummary(); return; } const ph = G.phases.find(p => p.id === G.sel.phaseId); if (!G.sel.catId) { el.innerHTML = renderPhaseOverview(ph); return; } const cat = G.cats.find(c => c.id === G.sel.catId); if (!G.sel.bucket) { el.innerHTML = renderCatOverview(ph, cat); return; } el.innerHTML = renderBucketDetail(ph, cat, G.sel.bucket); } function renderSummary() { const pp = G.data.progress; const pct = Math.round(pp?.progress_pct ?? pp?.global_pct ?? 0); const host = G.data.host; const cpu = fmt(host?.cpu?.usage_pct); const ram = fmt(host?.memory?.usage_pct ?? host?.ram?.usage_pct); const disk = fmt(host?.disk?.usage_pct); return `
Devon Operator Panel
Select a phase in the tree to navigate. Canon leads. Runtime validates. UI renders.
Progress
${pct}%
CPU
${cpu}
Memory
${ram}
Disk
${disk}
`; } function renderPhaseOverview(ph) { const cats = catsForPhase(ph.id); const cards = cats.map(c => `
${c.title}
${c.sub || ''}
`).join(''); return `
${ph.step}
${ph.name}
${ph.summary || ''}
${cards}
`; } function renderCatOverview(ph, cat) { const bkCards = BUCKETS.map((bk, i) => `
${String(i+1).padStart(2,'0')} ${bk} ${BUCKET_DESC[bk]}
`).join(''); return `
${ph.step} / ${cat.title}
${cat.title}
${cat.sub || ''}
Process Buckets
${bkCards}
`; } function renderBucketDetail(ph, cat, bucket) { const phIdx = G.phases.findIndex(p => p.id === ph.id); const bkIdx = BUCKETS.indexOf(bucket); const docs = cat.docs || []; const items = docs.map(doc => { const st = docStatus(doc); const cls = st === 'PASS' ? 'item-ok' : st === 'FAIL' ? 'item-fail' : ''; const ico = st === 'PASS' ? '✓' : st === 'FAIL' ? '✗' : '○'; return `
${ico}
${doc.title}
${doc.role || ''}
${doc.path || ''}
${st}
`; }).join(''); const phPct = Math.round((phIdx / G.phases.length) * 100); const bkPct = Math.round((bkIdx / BUCKETS.length) * 100); return `
${ph.step} / ${cat.title} / ${bucket}
${ph.step}${bucket}
${ph.step}/${cat.title}
${cat.title}
${BUCKET_DESC[bucket]}
Phase ${phIdx + 1} of ${G.phases.length}
${ph.name}
Bucket ${bkIdx + 1} of ${BUCKETS.length}
${bucket}
Authority Documents — ${cat.title}
${items || '
No documents registered for this category.
'}
`; } function docStatus(doc) { const rows = G.data.runtime?.rows || G.data.runtime?.runtime_rows || []; if (!rows.length) return 'PLANNED'; const name = (doc.path || '').split('/').pop().replace(/\.(json|md)$/, '').toLowerCase(); const match = rows.find(r => (r.label || r.subcategory || r.component || '').toLowerCase().includes(name) ); if (!match) return 'PLANNED'; const s = (match.status || '').toUpperCase(); return s === 'PASS' || s === 'FAIL' || s === 'MISSING' ? s : 'PLANNED'; } function fmt(v) { if (v == null) return '—'; return Math.round(v) + '%'; } function renderFooter() { const el = document.getElementById('dp-footer'); if (el) el.textContent = 'loaded: ' + new Date().toLocaleTimeString(); } /* ── SIDEBAR & DRAWER ─────────────────────────────── */ function bindSidebar() { document.querySelectorAll('.ib[data-view]').forEach(btn => { btn.addEventListener('click', () => openDrawer(btn.dataset.view)); }); document.getElementById('btn-docs')?.addEventListener('click', () => { window.open('../docs/', '_blank'); }); document.getElementById('btn-monitor')?.addEventListener('click', () => { const ph10 = G.phases.find(p => p.id === 'phase-10'); if (!ph10) return; G.openPhases.add('phase-10'); G.sel.phaseId = 'phase-10'; G.sel.catId = null; G.sel.bucket = null; renderTree(); renderDetail(); }); document.getElementById('dp-drawer-close')?.addEventListener('click', closeDrawer); } async function openDrawer(url) { const drawer = document.getElementById('dp-drawer'); const pre = document.getElementById('dp-drawer-pre'); drawer.classList.add('open'); pre.textContent = 'Loading…'; try { const data = await fetch(url).then(r => r.json()); pre.textContent = JSON.stringify(data, null, 2); } catch (e) { pre.textContent = 'Error: ' + e.message; } } function closeDrawer() { document.getElementById('dp-drawer').classList.remove('open'); } /* ── CAT CARD CLICK (phase overview) ─────────────── */ document.addEventListener('click', e => { const cc = e.target.closest('.cat-card'); if (cc) { G.sel.phaseId = cc.dataset.ph; G.sel.catId = cc.dataset.cat; G.sel.bucket = null; G.openPhases.add(cc.dataset.ph); G.openCats.add(cc.dataset.cat); renderTree(); renderDetail(); } const bc = e.target.closest('.bk-card'); if (bc) { G.sel.phaseId = bc.dataset.ph; G.sel.catId = bc.dataset.cat; G.sel.bucket = bc.dataset.bk; G.openPhases.add(bc.dataset.ph); G.openCats.add(bc.dataset.cat); renderTree(); renderDetail(); } }); document.addEventListener('DOMContentLoaded', () => { init(); bindSidebar(); }); ===== /home/yeff/public_html/devon/panel/assets/js/panel.runtime.fix.20260408_1.js ===== (function () { const DATA_BASE = "data/"; const els = { projectDonut: document.getElementById("project-donut"), globalPills: document.getElementById("global-pills"), stageNav: document.getElementById("stage-nav"), heroTitle: document.getElementById("hero-title"), heroSubtitle: document.getElementById("hero-subtitle"), stageOrderBadge: document.getElementById("stage-order-badge"), selectedStageName: document.getElementById("selected-stage-name"), selectedStageDesc: document.getElementById("selected-stage-desc"), selectedStageTotal: document.getElementById("selected-stage-total"), selectedStageDonut: document.getElementById("selected-stage-donut"), selectedRuntimeBadge: document.getElementById("selected-runtime-badge"), selectedPipelineSteps: document.getElementById("selected-pipeline-steps"), hostStatusBadge: document.getElementById("host-status-badge"), dockerStatusBadge: document.getElementById("docker-status-badge"), hostRuntimeGrid: document.getElementById("host-runtime-grid"), dockerRuntimeGrid: document.getElementById("docker-runtime-grid"), pipelineRuntimeCount: document.getElementById("pipeline-runtime-count"), pipelineRuntimeBoard: document.getElementById("pipeline-runtime-board"), drawerLabel: document.getElementById("drawer-label"), jsonViewer: document.getElementById("json-viewer"), btnOpenManifest: document.getElementById("btn-open-manifest"), btnOpenContract: document.getElementById("btn-open-contract"), btnOpenHost: document.getElementById("btn-open-host"), btnOpenDocker: document.getElementById("btn-open-docker"), btnOpenRuntime: document.getElementById("btn-open-runtime"), dataViewerModal: document.getElementById("data-viewer-modal") }; const state = { manifest: null, contentIndex: null, navigationSpec: null, pipelines: null, hostRuntime: null, dockerRuntime: null, runtimeStatus: null, projectProgress: null, selectedStage: null }; function safeUpper(v) { return String(v || "MISSING").trim().toUpperCase(); } function getBadgeClass(status) { const s = safeUpper(status); if (s === "PASS" || s === "OK" || s === "SUCCESS") return "pass"; if (s === "FAIL" || s === "ERROR") return "fail"; if (s === "RUNNING") return "running"; if (s === "PENDING") return "pending"; return "missing"; } function setBadge(el, status, text) { if (!el) return; el.className = "badge " + getBadgeClass(status); el.textContent = text || safeUpper(status); } function normStageKey(value) { return String(value || "") .trim() .toLowerCase() .replace(/[^a-z0-9]+/g, "_") .replace(/^_+|_+$/g, ""); } function titleize(value) { return String(value || "") .replace(/[_-]+/g, " ") .replace(/\b\w/g, function (m) { return m.toUpperCase(); }); } async function getJson(name) { const res = await fetch(DATA_BASE + name, { cache: "no-store" }); if (!res.ok) throw new Error(name + " -> HTTP " + res.status); return res.json(); } async function getJsonOptional(name) { try { return await getJson(name); } catch (err) { return null; } } function openJson(label, payload) { if (els.drawerLabel) els.drawerLabel.textContent = label || "data"; if (els.jsonViewer) { try { els.jsonViewer.textContent = JSON.stringify(payload, null, 2); } catch (e) { els.jsonViewer.textContent = String(payload); } } if (els.dataViewerModal) { els.dataViewerModal.hidden = false; els.dataViewerModal.style.display = "block"; } } function donutMarkup(pct, label) { const value = Math.max(0, Math.min(100, Number(pct || 0))); const r = 54; const c = 2 * Math.PI * r; const dash = (value / 100) * c; return [ '
', '', '
', '' + value + '%', '' + (label || "progress") + '', '
', '
' ].join(""); } function missingDonutMarkup(label) { return donutMarkup(0, label || "missing"); } function metricBox(label, value, sub) { return [ '
', '
' + label + '
', '
' + value + '
', '
' + sub + '
', '
' ].join(""); } function valueOrDash(value, suffix) { if (value === null || value === undefined || value === "") return "—"; return String(value) + String(suffix || ""); } function runtimeStatusOf(row) { if (!row || typeof row !== "object") return "MISSING"; return safeUpper( row.runtime_status ?? row.status ?? row.state ?? row.overall_status ?? "MISSING" ); } function stageRows(stageKey) { const snapshot = ((state.runtimeStatus && state.runtimeStatus.runtime_snapshot) || []); return snapshot.filter(function (row) { return normStageKey(row.deployment_stage) === normStageKey(stageKey); }); } function stageRollup(stageKey) { return stageRows(stageKey).find(function (row) { return String(row.row_kind || "") === "stage_rollup"; }) || null; } function renderGlobal() { const snapshot = ((state.runtimeStatus && state.runtimeStatus.runtime_snapshot) || []); const counts = snapshot.reduce(function (acc, row) { const s = runtimeStatusOf(row); acc[s] = (acc[s] || 0) + 1; return acc; }, { PASS: 0, FAIL: 0, MISSING: 0, RUNNING: 0, PENDING: 0 }); if (els.globalPills) { els.globalPills.innerHTML = [ 'PASS ' + (counts.PASS || 0) + '', 'FAIL ' + (counts.FAIL || 0) + '', 'MISSING ' + (counts.MISSING || 0) + '', 'RUNNING ' + (counts.RUNNING || 0) + '', 'PENDING ' + (counts.PENDING || 0) + '' ].join(""); } const gp = state.projectProgress && state.projectProgress.global_project_progress; if (els.projectDonut) { if (gp && typeof gp.progress_pct === "number") { els.projectDonut.innerHTML = donutMarkup(Math.round(gp.progress_pct), "project completion"); } else { els.projectDonut.innerHTML = missingDonutMarkup("project completion"); } } } function renderNav() { if (!els.stageNav) return; const items = ((state.contentIndex && state.contentIndex.content_index) || []) .filter(function (item) { return !!item.stage_key; }) .sort(function (a, b) { return Number(a.deployment_order || 0) - Number(b.deployment_order || 0); }); els.stageNav.innerHTML = ""; items.forEach(function (item) { const stageKey = normStageKey(item.stage_key); const btn = document.createElement("button"); btn.type = "button"; btn.className = "stage-btn"; btn.dataset.stageKey = stageKey; btn.innerHTML = [ '', '' + item.label + '', 'Order ' + item.deployment_order + '', '' ].join(""); btn.addEventListener("click", function () { selectStage(stageKey); }); els.stageNav.appendChild(btn); }); } function renderHostRuntime() { const host = (state.hostRuntime && state.hostRuntime.host_snapshot) || {}; setBadge(els.hostStatusBadge, host.overall_status || "MISSING", safeUpper(host.overall_status || "MISSING")); const cpu = host.cpu || {}; const memory = host.memory || {}; const disk = host.disk || {}; const load = host.load || {}; if (els.hostRuntimeGrid) { els.hostRuntimeGrid.innerHTML = [ metricBox("CPU", valueOrDash(cpu.usage_pct, "%"), "cores: " + valueOrDash(cpu.core_count, "")), metricBox("Memory", valueOrDash(memory.usage_pct, "%"), valueOrDash(memory.used_mb, " MB") + " / " + valueOrDash(memory.total_mb, " MB")), metricBox("Disk", valueOrDash(disk.usage_pct, "%"), valueOrDash(disk.used_gb, " GB") + " / " + valueOrDash(disk.total_gb, " GB")), metricBox("Load 1m", valueOrDash(load.load_1m, ""), "5m: " + valueOrDash(load.load_5m, "") + " | 15m: " + valueOrDash(load.load_15m, "")) ].join(""); } } function renderDockerRuntime() { const runtime = (state.dockerRuntime && state.dockerRuntime.runtime_snapshot) || {}; setBadge(els.dockerStatusBadge, runtime.overall_status || "MISSING", safeUpper(runtime.overall_status || "MISSING")); const engine = runtime.docker_engine || {}; const compose = runtime.compose || {}; const images = runtime.images || {}; const volumes = runtime.volumes || {}; const networks = runtime.networks || {}; const containers = Array.isArray(runtime.containers) ? runtime.containers : []; if (els.dockerRuntimeGrid) { els.dockerRuntimeGrid.innerHTML = [ metricBox("Engine", engine.installed === true ? "installed" : (engine.installed === false ? "not installed" : "MISSING"), "version: " + valueOrDash(engine.version, "")), metricBox("Compose", compose.installed === true ? "installed" : (compose.installed === false ? "not installed" : "MISSING"), "version: " + valueOrDash(compose.version, "")), metricBox("Containers", String(containers.length), "observable list"), metricBox("Images", valueOrDash(images.total, ""), "Volumes: " + valueOrDash(volumes.total, "") + " | Networks: " + valueOrDash(networks.total, "")) ].join(""); } } function renderPipelineBoard() { const items = ((state.runtimeStatus && state.runtimeStatus.runtime_snapshot) || []); if (els.pipelineRuntimeCount) { els.pipelineRuntimeCount.textContent = items.length + " runtime rows"; } if (!els.pipelineRuntimeBoard) return; if (!items.length) { els.pipelineRuntimeBoard.innerHTML = '

No runtime rows

Status is MISSING.

'; return; } els.pipelineRuntimeBoard.innerHTML = items.slice(0, 24).map(function (item) { const status = runtimeStatusOf(item); return [ '
', '

' + titleize(item.deployment_stage || "runtime") + ' / ' + titleize(item.technology || item.subcategory || "item") + '

', '

Kind: ' + titleize(item.row_kind || "runtime_row") + '

', '
', '' + status + '', '' + valueOrDash(item.progress_pct, "%") + '', '
', '
' ].join(""); }).join(""); } function renderSelectedPipeline(stageKey, rollup) { if (!els.selectedPipelineSteps) return; const pipelines = (state.pipelines && state.pipelines.pipelines) || {}; const contract = pipelines[stageKey] || {}; const seq = Array.isArray(contract.sequence) ? contract.sequence : []; const stepStatuses = rollup && Array.isArray(rollup.step_statuses) ? rollup.step_statuses : []; if (!seq.length) { els.selectedPipelineSteps.innerHTML = '
  • No pipeline contract foundmissing
  • '; return; } els.selectedPipelineSteps.innerHTML = seq.map(function (step) { const observed = stepStatuses.find(function (x) { return normStageKey(x.step || x.name) === normStageKey(step); }); const status = observed ? safeUpper(observed.status) : "MISSING"; return [ '
  • ', '' + titleize(step) + '', '' + status + '', '
  • ' ].join(""); }).join(""); } function selectStage(stageKey) { state.selectedStage = normStageKey(stageKey); const items = ((state.contentIndex && state.contentIndex.content_index) || []); const navItem = items.find(function (item) { return normStageKey(item.stage_key) === state.selectedStage; }) || null; const rollup = stageRollup(state.selectedStage); const pct = rollup && typeof rollup.progress_pct === "number" ? Math.round(rollup.progress_pct) : 0; const totalSteps = rollup && Array.isArray(rollup.step_statuses) ? rollup.step_statuses.length : 0; if (els.heroTitle) els.heroTitle.textContent = navItem ? navItem.label : titleize(state.selectedStage); if (els.heroSubtitle) { els.heroSubtitle.textContent = "Runtime-backed stage view. Contract and runtime data are rendered from panel exports only."; } if (els.stageOrderBadge) { els.stageOrderBadge.textContent = navItem ? ("ORDER " + navItem.deployment_order) : "—"; } if (els.selectedStageName) { els.selectedStageName.textContent = navItem ? navItem.label : titleize(state.selectedStage); } if (els.selectedStageDesc) { els.selectedStageDesc.textContent = "Stage completion is read from runtime_status stage_rollup.progress_pct."; } if (els.selectedStageTotal) { els.selectedStageTotal.innerHTML = totalSteps ? (totalSteps + ' required steps
    in contract') : 'MISSING stage rollup
    in runtime'; } if (els.selectedStageDonut) { els.selectedStageDonut.innerHTML = donutMarkup(pct, "stage completion"); } if (els.selectedRuntimeBadge) { setBadge(els.selectedRuntimeBadge, rollup ? runtimeStatusOf(rollup) : "MISSING", rollup ? runtimeStatusOf(rollup) : "MISSING"); } renderSelectedPipeline(state.selectedStage, rollup); document.querySelectorAll(".stage-btn").forEach(function (btn) { btn.classList.toggle("active", btn.dataset.stageKey === state.selectedStage); }); } function wireButtons() { [