===== /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.
`;
}
function renderPhaseOverview(ph) {
const cats = catsForPhase(ph.id);
const cards = cats.map(c => `
${c.title}
${c.sub || ''}
`).join('');
return ``;
}
function renderCatOverview(ph, cat) {
const bkCards = BUCKETS.map((bk, i) => `
${String(i+1).padStart(2,'0')}
${bk}
${BUCKET_DESC[bk]}
`).join('');
return `
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}
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() {
[