mesh-cathedral / src /codex.js
betterwithage's picture
feat(mesh): live 45-kernel observability — per-kernel status colours (green/amber/red from /api/<organ>/v3/kernels), heartbeat pulse on each dot, last-5-heartbeats in kernel side panel, 45-kernel signed-receipt ticker. ADDITIVE. Doctrine v11 749/14/163. Sign: Yachay
320c18f verified
// Codex panel — click any chakra -> its codex (recipes/theses/formulas) opens in
// the side panel. Click a kernel dot -> that kernel's docs + recent live activity.
// Top-right "All Codices" -> unified search across all 5 chakras' codices.
// Doctrine v11 LOCKED. ZERO BANDAID — links are real; live activity shows honest state.
import { CHAKRAS, UNIVERSAL_KERNELS } from './config.js';
import { kernelEntry, lastHeartbeats } from './heartbeats.js';
let panel, body, closeBtn;
export function initCodex(){
panel = document.getElementById('panel');
body = document.getElementById('panelBody');
closeBtn = document.getElementById('panelClose');
closeBtn.onclick = closePanel;
document.getElementById('allCodices').onclick = openAllCodices;
addEventListener('keydown', e => { if (e.key==='Escape') closePanel(); });
}
function openPanel(){ panel.classList.add('open'); }
function closePanel(){ panel.classList.remove('open'); }
function chakraById(id){ return CHAKRAS.find(c => c.id===id); }
// ---------- chakra codex ----------
export function openChakraCodex(id){
const c = chakraById(id); if (!c) return;
const col = '#'+c.color.toString(16).padStart(6,'0');
const kernels = UNIVERSAL_KERNELS.map(k => `<span class="ktag">${k.name}</span>`).join('')
+ c.vertical.map(k => `<span class="ktag vert">${k.name}</span>`).join('');
body.innerHTML = `
<h2 style="color:${col}">${c.label}</h2>
<div class="sub">${c.glyph} · 9 kernels (7 universal + 2 vertical)</div>
<h3>Codex</h3>
${c.codex.map(x => `<a class="link" href="${x.href}" target="_blank" rel="noopener">${x.label}</a>`).join('')}
<h3>Kernels (wheel)</h3>
<div id="kernelList">${kernels}</div>
<h3>Live</h3>
<div class="kv" id="liveBox">polling ${c.label}…</div>
`;
openPanel();
pollChakraLive(c);
}
async function pollChakraLive(c){
const boxEl = document.getElementById('liveBox');
try {
const r = await fetch(c.health.url, { cache:'no-store' });
if (!r.ok){ boxEl.textContent = `health → HTTP ${r.status}`; return; }
let j; try { j = await r.json(); } catch(e){ boxEl.textContent='health → 200 (non-JSON)'; return; }
const bits = [];
if (j.status) bits.push(`status: <b>${j.status}</b>`);
if (j.version) bits.push(`version: ${j.version}`);
if (j.doctrine) bits.push(`doctrine: ${j.doctrine}`);
if (j.numbers) bits.push(`numbers: ${j.numbers.declarations}/${j.numbers.axioms}/${j.numbers.sorries}`);
else if (j.declarations!==undefined) bits.push(`decl: ${j.declarations} · sorries: ${j.sorries}`);
if (j.gates) bits.push(`gates: ${j.gates}`);
if (j.traceparent_propagating) bits.push('traceparent: LIVE');
boxEl.innerHTML = bits.length ? bits.join('<br>') : '200 OK (no detail fields)';
} catch(e){ boxEl.textContent = 'flagship unreachable (honest offline state)'; }
}
// ---------- kernel panel ----------
const STATUS_LABEL = { green:'● live', amber:'● degraded', red:'● offline' };
const STATUS_HEX = { green:'#34d399', amber:'#fbbf24', red:'#f87171' };
export function openKernelPanel(c, kernel, vertical){
const col = '#'+c.color.toString(16).padStart(6,'0');
const sub = vertical ? 'vertical kernel' : `universal kernel · substrate: ${kernel.substrate}`;
const e = kernelEntry(c.id, kernel.name);
const st = e ? e.status : 'red';
const stHex = STATUS_HEX[st] || '#6b7280';
const ago = (e && e.agoSec != null) ? `${e.agoSec}s ago` : '—';
const ticks = (e && e.ticks != null) ? e.ticks.toLocaleString() : '—';
const endpoint = `${c.base}/api/${c.id}/v3/kernels/${kernel.name.toLowerCase()}`;
body.innerHTML = `
<h2 style="color:${col}">${kernel.name}</h2>
<div class="sub">${c.label} · ${sub}</div>
<div class="kv" style="display:flex;gap:14px;flex-wrap:wrap;margin:8px 0 2px">
<span style="color:${stHex};font-weight:700">${STATUS_LABEL[st]||'●'}</span>
<span>last beat: <b>${ago}</b></span>
<span>signed receipts: <b>${ticks}</b></span>
</div>
<h3>What it does</h3>
<div class="kv">${kernel.does}</div>
${kernel.substrate ? `<h3>Substrate</h3><div class="kv">package: <b>${kernel.substrate}</b></div>` : ''}
<h3>Last 5 heartbeats</h3>
<div class="kv" id="hbBox">polling ${kernel.name}…</div>
<h3>Kernel endpoint</h3>
<a class="link" href="${endpoint}" target="_blank" rel="noopener">GET /api/${c.id}/v3/kernels/${kernel.name.toLowerCase()}</a>
<h3>Chakra codex</h3>
${c.codex.map(x => `<a class="link" href="${x.href}" target="_blank" rel="noopener">${x.label}</a>`).join('')}
<h3>Flagship health</h3>
<div class="kv" id="liveBox">polling ${c.label}…</div>
<p style="font-size:11px;color:#6b7c90;margin-top:18px">Each kernel is a perpetual OODA loop (observe→decide→act→sign) rooted in a hash-linked, DSSE-signed codex. The Ouroboros loop threads SIGN→GATE→CHAIN→MEMORY→REPLAY through every chakra. Doctrine v11 LOCKED (749/14/163).</p>
`;
openPanel();
pollKernelHeartbeats(c, kernel);
pollChakraLive(c);
}
async function pollKernelHeartbeats(c, kernel){
const box = document.getElementById('hbBox');
if (!box) return;
const beats = await lastHeartbeats(c.id, kernel.name, 5);
if (!document.getElementById('hbBox')) return; // panel changed while awaiting
if (!beats.length){ box.textContent = 'no heartbeats yet (or flagship unreachable — honest offline state)'; return; }
box.innerHTML = beats.map(b => {
// codex entries may wrap the receipt under .payload / .data / be the receipt itself
const rcpt = b.payload || b.data || b.receipt || b;
const tick = rcpt.tick != null ? `#${rcpt.tick}` : '';
const ts = rcpt.ts || b.ts || b.created_at || '';
const sum = rcpt.summary || rcpt.did_work === false ? (rcpt.summary || 'no-op') : (rcpt.summary || 'tick');
const signed = (rcpt.signed_payload || rcpt.signatures || (rcpt.signed_payload && rcpt.signed_payload.signatures)) ? '🔏' : '';
return `<div style="padding:6px 0;border-bottom:1px solid #ffffff10;font-size:12px">
<b>${tick}</b> <span style="color:#9fb0c4">${String(ts).replace('T',' ').replace('Z','')}</span> ${signed}<br>
<span style="color:#cfe0f2">${sum}</span></div>`;
}).join('');
}
// ---------- unified "All Codices" search ----------
function openAllCodices(){
const all = CHAKRAS.flatMap(c => c.codex.map(x => ({...x, chakra:c.label, color:c.color, id:c.id})));
body.innerHTML = `
<h2>All Codices</h2>
<div class="sub">Unified search across all 5 chakras (${all.length} codex links · 45 kernels)</div>
<input id="codexSearch" placeholder="filter recipes / formulas / theses…"
style="width:100%;padding:10px;border-radius:8px;border:1px solid #ffffff22;background:#0a0e14;color:#e8eef6;font-size:14px;margin-bottom:12px" />
<div id="codexResults"></div>
`;
openPanel();
const input = document.getElementById('codexSearch');
const out = document.getElementById('codexResults');
const render = (q='') => {
const ql = q.toLowerCase();
const rows = all.filter(x => (x.label+x.chakra).toLowerCase().includes(ql));
out.innerHTML = rows.map(x => {
const col = '#'+x.color.toString(16).padStart(6,'0');
return `<a class="link" href="${x.href}" target="_blank" rel="noopener">
<span style="color:${col};font-weight:700">${x.chakra}</span> · ${x.label}</a>`;
}).join('') || '<div class="kv">no match</div>';
};
render();
input.oninput = () => render(input.value);
input.focus();
}