// Visual Control Plane — client (vanilla JS, D3 from CDN) // Design note: KB data flows from local jsonl files we control, but we // still use DOM methods (createElement/textContent) for every // data-derived node to satisfy static analysis and keep a clean XSS // boundary if the UI ever gets exposed. const POLL_MS = 3000; const state = { view: "map", overlay: "status", selected: null, services: null, reviews: [], metrics: [], overrides: [], trust: [], findings: [], }; // ───── view switcher ───── document.querySelectorAll("#views button").forEach(b => { b.addEventListener("click", () => { document.querySelectorAll("#views button").forEach(x => x.classList.remove("on")); b.classList.add("on"); state.view = b.dataset.view; document.querySelectorAll(".view").forEach(v => v.classList.remove("on")); document.getElementById(`view-${state.view}`).classList.add("on"); renderView(); }); }); document.querySelectorAll("#overlay-controls button").forEach(b => { b.addEventListener("click", () => { document.querySelectorAll("#overlay-controls button").forEach(x => x.classList.remove("on")); b.classList.add("on"); state.overlay = b.dataset.ov; if (state.services) drawMap(state.services); }); }); // ───── helpers ───── function el(tag, opts = {}, ...kids) { const n = document.createElement(tag); if (opts.className) n.className = opts.className; if (opts.text != null) n.textContent = String(opts.text); if (opts.data) for (const k in opts.data) n.dataset[k] = opts.data[k]; if (opts.attrs) for (const k in opts.attrs) n.setAttribute(k, opts.attrs[k]); if (opts.style) for (const k in opts.style) n.style[k] = opts.style[k]; for (const k of kids) if (k != null) n.append(k); return n; } function clear(node) { while (node.firstChild) node.removeChild(node.firstChild); } function row(k, v, valClass) { const r = el("div", { className: "ctx-row" }); r.append(el("span", { className: "k", text: k })); const vv = el("span", { className: "v" + (valClass ? " " + valClass : ""), text: String(v ?? "-") }); r.append(vv); return r; } function short(v) { if (v == null) return "-"; if (typeof v === "object") return JSON.stringify(v).slice(0, 80); return String(v).slice(0, 80); } // ───── polling ───── async function poll() { try { const [svc, rev, met, ov, tr, fin] = await Promise.all([ fetch("/data/services").then(r => r.json()), fetch("/data/reviews?tail=80").then(r => r.json()), fetch("/data/metrics").then(r => r.json()), fetch("/data/overrides").then(r => r.json()), fetch("/data/trust").then(r => r.json()), fetch("/data/findings").then(r => r.json()), ]); state.services = svc; state.reviews = Array.isArray(rev) ? rev : []; state.metrics = Array.isArray(met) ? met : []; state.overrides = Array.isArray(ov) ? ov : []; state.trust = Array.isArray(tr) ? tr : []; state.findings = Array.isArray(fin) ? fin : []; document.getElementById("build-ts").textContent = new Date(svc.ts).toLocaleTimeString(); svc.nodes.forEach(n => { const chip = document.querySelector(`.hbchip[data-svc="${n.id}"]`); if (chip) chip.setAttribute("data-status", n.status); }); renderView(); renderContext(); pollStream(); } catch (e) { console.error("poll error", e); } } async function pollStream() { try { const j = await fetch("/data/scrum_log").then(r => r.json()); if (!j.lines) return; document.getElementById("stream-file").textContent = j.file ? j.file.split("/").pop() : "—"; const body = document.getElementById("stream-body"); clear(body); j.lines.slice(-30).forEach(line => { const cls = /✓ ACCEPTED/.test(line) ? "ok" : /✗ thin/.test(line) ? "thin" : /error|failed|FAIL/i.test(line) ? "err" : /^\[scrum\] file:/.test(line) ? "head" : "info"; body.append(el("div", { className: "sline " + cls, text: line })); }); body.scrollTop = body.scrollHeight; } catch {} } function renderView() { if (!state.services) return; if (state.view === "map") drawMap(state.services); else if (state.view === "trace") drawTrace(); else if (state.view === "trajectory") drawTrajectory(); else if (state.view === "metrics") drawMetrics(); else if (state.view === "kb") drawKB(); else if (state.view === "console") drawConsole(); } // ───── MAP ───── const NODES_STATIC = [ { id: "gateway", x: 0.5, y: 0.15 }, { id: "sidecar", x: 0.2, y: 0.3 }, { id: "observer", x: 0.8, y: 0.3 }, { id: "mcp", x: 0.85, y: 0.1 }, { id: "context7", x: 0.15, y: 0.1 }, { id: "journal", x: 0.35, y: 0.55 }, { id: "vectord", x: 0.5, y: 0.5 }, { id: "playbook", x: 0.65, y: 0.55 }, { id: "agent", x: 0.5, y: 0.75 }, { id: "usage", x: 0.2, y: 0.75 }, ]; const EDGES = [ ["gateway","sidecar"],["gateway","observer"],["gateway","mcp"],["gateway","context7"], ["gateway","journal"],["gateway","vectord"],["gateway","playbook"],["gateway","agent"],["gateway","usage"], ["vectord","playbook"],["agent","vectord"],["observer","playbook"],["sidecar","vectord"], ]; function drawMap(svc) { const svg = d3.select("#map"); const box = svg.node().getBoundingClientRect(); const W = box.width, H = box.height; svg.selectAll("*").remove(); const statusMap = {}; [...svc.nodes, ...svc.subsystems].forEach(n => statusMap[n.id] = n); svg.selectAll(".edge").data(EDGES).enter().append("line") .attr("class", d => "edge" + (overlayEdgeActive(d) ? " active" : "")) .attr("x1", d => nodePos(d[0]).x * W).attr("y1", d => nodePos(d[0]).y * H) .attr("x2", d => nodePos(d[1]).x * W).attr("y2", d => nodePos(d[1]).y * H); const g = svg.selectAll(".node").data(NODES_STATIC).enter().append("g") .attr("class", "node") .attr("transform", d => `translate(${d.x * W}, ${d.y * H})`) .on("click", (_ev, d) => { state.selected = { type:"node", id:d.id }; renderContext(); drawMap(svc); }); g.append("circle") .attr("class", d => "node-circle" + (state.selected?.type==="node" && state.selected.id===d.id ? " node-selected" : "")) .attr("r", d => nodeRadius(d, statusMap)) .attr("fill", d => nodeColor(d, statusMap)); // SVG tooltip — hover a node, browser shows a native tooltip with // what this node DOES, not just its name. g.append("title").text(d => nodeTooltip(d.id)); g.append("text").attr("class","node-label").attr("y", -30).text(d => nodeLabel(d.id)); g.append("text").attr("class","node-sub").attr("y", 40).text(d => nodeSub(d, statusMap)); } function nodePos(id) { return NODES_STATIC.find(x => x.id === id) ?? { x:0, y:0 }; } function nodeLabel(id) { return ({gateway:"GATEWAY",sidecar:"SIDECAR",observer:"OBSERVER",mcp:"MCP",context7:"CTX7", journal:"JOURNAL",vectord:"VECTORD",playbook:"PLAYBOOK",agent:"AUTOTUNE",usage:"USAGE"})[id] ?? id; } function nodeRadius(d, m) { const n = m[d.id]; if (state.overlay === "activity") { if (d.id === "journal" && n?.stats?.total_events_created != null) return 14 + Math.min(20, Math.log2(n.stats.total_events_created + 1) * 2); if (d.id === "vectord" && n?.stats?.count != null) return 14 + Math.min(20, Math.log2(n.stats.count + 1) * 2); if (d.id === "playbook" && n?.stats?.total != null) return 14 + Math.min(20, Math.log2(n.stats.total + 1)); if (d.id === "observer" && n?.stats?.total != null) return 14 + Math.min(20, Math.log2(n.stats.total + 1)); if (d.id === "usage" && n?.stats?.requests != null) return 14 + Math.min(20, Math.log2(n.stats.requests + 1) * 2); } return 18; } function nodeColor(d, m) { const n = m[d.id]; const ov = state.overlay; if (ov === "status" || ov === "activity") { const st = n?.status ?? (n?.stats ? "healthy" : "unknown"); return { healthy:"#3eed86", degraded:"#ffbf3c", down:"#ff4d6e", unknown:"#525c6f" }[st] ?? "#525c6f"; } if (ov === "confidence") { const c = recentAvgConfidence(d.id); if (c == null) return "#525c6f"; if (c >= 88) return "#3eed86"; if (c >= 70) return "#55c5ff"; if (c >= 50) return "#ffbf3c"; return "#ff4d6e"; } if (ov === "gradient") { const t = recentGradientTier(d.id); return t ? ({auto:"#3eed86",dry_run:"#55c5ff",simulation:"#ffbf3c",block:"#ff4d6e"}[t] ?? "#525c6f") : "#525c6f"; } if (ov === "verdict") { const v = recentVerdict(d.id); return {pass:"#3eed86",needs_patch:"#ff9f43",fail:"#ff4d6e"}[v] ?? "#525c6f"; } return "#55c5ff"; } function nodeSub(d, m) { const n = m[d.id]; if (!n) return "…"; if (d.id === "journal" && n.stats) return `${n.stats.total_events_created ?? 0} events · ${n.stats.persisted_files ?? 0} parquet`; if (d.id === "usage" && n.stats) return `${n.stats.requests ?? 0} requests · ${Math.round((n.stats.total_tokens ?? 0)/1000)}k tokens`; if (d.id === "vectord" && typeof n.stats === "object" && n.stats) return `${n.stats.count ?? 0} indexes`; if (d.id === "playbook" && n.stats) return `${n.stats.active ?? 0} active · ${n.stats.retired ?? 0} retired`; if (d.id === "agent" && n.stats) return `${n.stats.trials_run ?? 0} trials · ${n.stats.promotions ?? 0} promotions`; if (d.id === "observer" && n.stats) return `${n.stats.total ?? 0} observed ops`; return String(n.status ?? ""); } // Describes what each node DOES — shown as SVG tooltip. function nodeTooltip(id) { return ({ gateway: "GATEWAY — Rust/Axum HTTP on :3100. Every external call enters here: /v1/chat, /ingest, /query, /tools, /journal, /vectors. Also hosts gRPC on :3101.", sidecar: "SIDECAR — Python FastAPI on :3200. Adapter from Rust to local Ollama (:11434). Handles /embed /generate /rerank. Stateless.", observer: "OBSERVER — Bun on :3800. Ring buffer of recent ops across the system. Feeds analyzeErrors + PLAYBOOK_BUILDER loops. Scrum events now land here (P45 fix).", mcp: "MCP — Bun on :3700. Model Context Protocol tool gateway. Agent-facing tool endpoints.", context7: "CONTEXT7 — Bun on :3900. Doc-drift resolver — checks playbook doc_refs against current docs for version drift (Phase 45 target).", journal: "JOURNAL — ADR-012 append-only mutation log inside the gateway. Every ingest/delta-write/tombstone should record here. Currently ~1 real event (P9-001 still mostly unwired).", vectord: "VECTORD — Embeddings store + HNSW index + autotune harness. The 'indexes' count = named vector indexes live right now (one per source × model_version).", playbook: "PLAYBOOK — Meta-index. Each entry = a successful past pattern + geo/role + 768d embedding. Active entries boost future vector-search results (Phase 19).", agent: "AUTOTUNE — Background agent that continuously proposes HNSW config trials, picks Pareto winners above min_recall, promotes, and rolls back. Self-tuning vector index.", usage: "USAGE — /v1/chat token counters. Tracks requests, prompt/completion tokens, per-provider breakdown. Grows with scrum + audit traffic.", })[id] ?? id; } function overlayEdgeActive(edge) { if (!state.reviews.length) return false; const latest = state.reviews[state.reviews.length - 1]; if (!latest?.reviewed_at) return false; const age = Date.now() - new Date(latest.reviewed_at).getTime(); if (age > 60000) return false; return edge.includes("gateway") && (edge.includes("observer") || edge.includes("vectord")); } function matchesNode(r, id) { if (!r?.file) return false; const f = r.file.toLowerCase(); if (id === "gateway") return f.includes("/gateway/"); if (id === "vectord") return f.includes("/vectord"); if (id === "journal") return f.includes("/journald"); if (id === "playbook")return f.includes("playbook_memory"); if (id === "sidecar") return f.includes("sidecar"); if (id === "agent") return f.includes("agent.rs") || f.includes("autotune"); return false; } function recentAvgConfidence(id) { const rs = state.reviews.filter(r => matchesNode(r, id)); const vs = rs.map(r => r.confidence_avg).filter(v => v != null); return vs.length ? vs.reduce((a,b)=>a+b,0)/vs.length : null; } function recentGradientTier(id) { const rs = state.reviews.filter(r => matchesNode(r, id)); const ts = rs.map(r => r.gradient_tier).filter(Boolean); return ts[ts.length - 1] ?? null; } function recentVerdict(id) { const rs = state.reviews.filter(r => matchesNode(r, id)); const vs = rs.map(r => r.verdict).filter(Boolean); return vs[vs.length - 1] ?? null; } // ───── CONTEXT ───── function renderContext() { const target = document.getElementById("ctx-target"); const body = document.getElementById("ctx-body"); clear(body); if (!state.selected) { target.textContent = "no selection"; body.append(el("div", { className: "ctx-hint", text: "Click a node or a file in KB to inspect. Context persists across view switches." })); body.append(el("div", { className: "ctx-section-hd", text: "System totals" })); appendSummaryKV(body); return; } if (state.selected.type === "node") renderNodeContext(state.selected.id, target, body); else if (state.selected.type === "file") renderFileContext(state.selected.id, target, body); } function appendSummaryKV(body) { const s = state.services; if (!s) { body.append(el("div", { className: "ctx-hint", text: "loading…" })); return; } const get = id => s.nodes.concat(s.subsystems).find(n => n.id === id); const journal = get("journal")?.stats ?? {}; const usage = get("usage")?.stats ?? {}; const playbook = get("playbook")?.stats ?? {}; const agent = get("agent")?.stats ?? {}; const observer = get("observer")?.stats ?? {}; body.append(row("scrum reviews", state.reviews.length)); body.append(row("journal events", journal.total_events_created ?? 0)); body.append(row("usage tokens", (usage.total_tokens ?? 0).toLocaleString())); body.append(row("playbook active", playbook.active ?? 0)); body.append(row("autotune trials", agent.trials_run ?? 0)); body.append(row("observer ops", observer.total ?? 0)); body.append(row("findings (h/m/l)", `${countFindingsSev("high")}/${countFindingsSev("medium")}/${countFindingsSev("low")}`)); } function countFindingsSev(sev) { let n = 0; for (const row of state.findings) for (const f of row.findings ?? []) if (f.severity === sev) n++; return n; } function renderNodeContext(id, target, body) { target.textContent = `NODE · ${id.toUpperCase()}`; const n = [...state.services.nodes, ...state.services.subsystems].find(x => x.id === id); if (n?.health) { body.append(el("div", { className: "ctx-section-hd", text: "Health" })); // Fix 2026-04-24: some /health endpoints return a plain string like // "lakehouse ok". Don't Object.entries() on strings — that iterates // characters. Detect primitive vs object explicitly. if (typeof n.health === "string" || typeof n.health === "number" || typeof n.health === "boolean") { body.append(row("response", String(n.health).slice(0, 80))); } else if (typeof n.health === "object" && n.health !== null) { Object.entries(n.health).slice(0, 8).forEach(([k,v]) => body.append(row(k, short(v)))); } } if (n?.stats) { body.append(el("div", { className: "ctx-section-hd", text: "Stats" })); if (typeof n.stats === "string") { body.append(row("raw", String(n.stats).slice(0, 80))); } else if (typeof n.stats === "object" && n.stats !== null) { Object.entries(n.stats).slice(0, 10).forEach(([k,v]) => body.append(row(k, short(v)))); } } const related = state.reviews.filter(r => matchesNode(r, id)).slice(-5).reverse(); if (related.length) { body.append(el("div", { className: "ctx-section-hd", text: "Recent reviews" })); related.forEach(r => { const rr = row(r.file.split("/").pop(), `${r.confidence_avg ?? "-"}% · ${r.alignment_score ?? "?"}/10`); rr.style.cursor = "pointer"; rr.addEventListener("click", () => { state.selected = { type:"file", id:r.file }; renderContext(); }); body.append(rr); }); } if (!body.firstChild) body.append(el("div", { className: "ctx-hint", text: "no data yet" })); } function renderFileContext(fpath, target, body) { target.textContent = fpath.split("/").slice(-3).join("/"); const fileReviews = state.reviews.filter(r => r.file === fpath).slice(-6); if (!fileReviews.length) { body.append(el("div", { className: "ctx-hint", text: `no reviews for ${fpath}` })); return; } const latest = fileReviews[fileReviews.length - 1]; const pillRow = el("div", { style: { paddingBottom: "6px" } }); if (latest.gradient_tier) pillRow.append(el("span", { className: `pill tier-${latest.gradient_tier}`, text: latest.gradient_tier })); if (latest.verdict) pillRow.append(el("span", { className: `pill ver-${latest.verdict}`, text: latest.verdict })); if (latest.output_format) pillRow.append(el("span", { className: `pill fmt-${latest.output_format}`, text: latest.output_format })); body.append(pillRow); const rows = [ ["file", fpath], ["score", latest.alignment_score != null ? `${latest.alignment_score}/10` : "-"], ["conf avg", latest.confidence_avg != null ? `${latest.confidence_avg}%` : "-"], ["conf min", latest.confidence_min != null ? `${latest.confidence_min}%` : "-"], ["findings", latest.findings_count ?? 0], ["critical", latest.critical_failures_count ?? 0], ["verified", latest.verified_components_count ?? 0], ["missing", latest.missing_components_count ?? 0], ["model", latest.accepted_model ?? "-"], ["attempts", latest.attempts_made ?? 1], ["tree split", latest.tree_split_fired ? "yes" : "no"], ]; rows.forEach(([k,v]) => body.append(row(k, short(v)))); body.append(el("div", { className: "ctx-section-hd", text: "Score history" })); fileReviews.forEach(r => body.append(row(new Date(r.reviewed_at).toLocaleTimeString(), `${r.alignment_score ?? "?"}/10 · ${r.confidence_avg ?? "-"}%`))); body.append(el("div", { className: "ctx-section-hd", text: "Preview" })); const pre = el("pre", { text: latest.suggestions_preview ?? "", style: { whiteSpace: "pre-wrap", fontFamily: "var(--mono)", fontSize: "10px", color: "var(--fg-dim)", maxHeight: "200px", overflowY: "auto" } }); body.append(pre); document.getElementById("stream-file").textContent = fpath.split("/").pop(); } // ───── TRACE ───── async function drawTrace() { const fpath = state.selected?.type === "file" ? state.selected.id : state.reviews[state.reviews.length-1]?.file; const tl = document.getElementById("trace-timeline"); const detail = document.getElementById("trace-detail"); clear(tl); clear(detail); document.getElementById("trace-file").textContent = fpath ?? "—"; if (!fpath) { tl.append(el("div", { className: "ctx-hint", text: "no file selected — pick one in KB view" })); return; } const r = await fetch(`/data/file/${encodeURIComponent(fpath)}`).then(r => r.json()); const history = r.history ?? []; document.getElementById("trace-runs").textContent = `${history.length} runs`; history.forEach((h, i) => { const node = el("div", { className: "trace-node" + (i === history.length - 1 ? " active" : "") }); node.append(el("div", { className: "tn-run", text: h.run_id })); node.append(el("div", { className: "tn-score", text: h.score != null ? String(h.score) : "?" })); node.append(el("div", { className: "tn-conf", text: `conf ${h.conf_avg ?? "-"}% · ${h.findings}f` })); node.append(el("div", { className: "tn-model", text: (h.model ?? "").split("/").pop() })); node.addEventListener("click", () => { tl.querySelectorAll(".trace-node").forEach(x => x.classList.remove("active")); node.classList.add("active"); clear(detail); detail.append(el("pre", { text: h.preview ?? "" })); }); tl.append(node); }); if (history.length) { clear(detail); detail.append(el("pre", { text: history[history.length-1].preview ?? "" })); } } // ───── TRAJECTORY — refactor signals + reverse index + per-file delta ───── let trajectorySearchTimer = null; document.getElementById("traj-search")?.addEventListener("input", (e) => { const q = e.target.value.trim(); clearTimeout(trajectorySearchTimer); trajectorySearchTimer = setTimeout(() => runReverseIndex(q), 300); }); async function runReverseIndex(query) { const body = document.getElementById("traj-body"); if (!query) { drawTrajectory(); return; } clear(body); const res = await fetch(`/data/search?q=${encodeURIComponent(query)}`).then(r => r.json()); const hdr = el("div", { className: "traj-section-head", text: `REVERSE INDEX · "${query}" · ${res.hits?.length ?? 0} hits` }); body.append(hdr); (res.hits ?? []).forEach(h => { const card = el("div", { className: "traj-hit" }); card.append(el("div", { className: "traj-hit-top" }, el("span", { className: "traj-hit-file", text: h.file }), el("span", { className: "traj-hit-meta", text: `${h.run_id} · ${(h.model ?? "").split("/").pop()}` }) )); card.append(el("div", { className: "traj-hit-snip", text: h.snippet })); card.addEventListener("click", () => { state.selected = { type: "file", id: `/home/profit/lakehouse/${h.file}` }; renderContext(); document.querySelector('#views button[data-view="trace"]').click(); }); body.append(card); }); } async function drawTrajectory() { const body = document.getElementById("traj-body"); clear(body); const statsEl = document.getElementById("traj-stats"); clear(statsEl); // SECTION 1 — refactor signals const sig = await fetch("/data/refactor_signals").then(r => r.json()); const sigs = sig.signals ?? []; const totalHits = sigs.reduce((a,s) => a + s.hits, 0); statsEl.textContent = `${sig.scanned ?? 0} files scanned · ${sigs.length} with refactor hints · ${totalHits} phrase hits total`; const sigHead = el("div", { className: "traj-section-head", text: "REFACTOR SIGNALS · files the scrum repeatedly flagged as dead / redundant / stub / needs-rewrite" }); body.append(sigHead); const explain = el("div", { className: "traj-section-explain", text: "Aggregates across all scrum iterations. A phrase hit = one time the reviewer used language like 'remove', 'duplicate', 'refactor', 'pseudocode', 'orphaned'. " + "Files near the top are the strongest refactor candidates — the scrum keeps calling them out. Click a row to jump to its per-iteration trace." }); body.append(explain); const table = el("div", { className: "traj-table" }); sigs.slice(0, 30).forEach(s => { const r = el("div", { className: "traj-row" }); r.append(el("div", { className: "traj-col-rank", text: String(sigs.indexOf(s) + 1) })); r.append(el("div", { className: "traj-col-file", text: s.file })); r.append(el("div", { className: "traj-col-hits", text: `${s.hits}×` })); const topPhrases = Object.entries(s.phrases).sort((a,b)=>b[1]-a[1]).slice(0,3) .map(([p,n]) => `${p} (${n})`).join(", "); r.append(el("div", { className: "traj-col-phrases", text: topPhrases })); r.append(el("div", { className: "traj-col-iters", text: `${s.iterations} iter` })); r.addEventListener("click", () => { state.selected = { type: "file", id: `/home/profit/lakehouse/${s.file}` }; renderContext(); document.querySelector('#views button[data-view="trace"]').click(); }); table.append(r); }); body.append(table); // SECTION 2 — per-file trajectory: pick the top-5 refactor candidates and // show their score/conf delta across iterations inline. if (sigs.length) { body.append(el("div", { className: "traj-section-head", text: "SCORE TRAJECTORY — top refactor candidates" })); const grid = el("div", { className: "traj-spark-grid" }); for (const s of sigs.slice(0, 6)) { const card = el("div", { className: "traj-spark" }); card.append(el("div", { className: "traj-spark-file", text: s.file })); // pull history const hist = await fetch(`/data/file/${encodeURIComponent("/home/profit/lakehouse/" + s.file)}`) .then(r => r.json()).catch(() => ({ history: [] })); const runs = hist.history ?? []; if (runs.length === 0) { card.append(el("div", { className: "traj-spark-empty", text: "no history" })); } else { const line = el("div", { className: "traj-spark-line" }); runs.forEach((h,i) => { const pt = el("div", { className: "traj-spark-pt" }); pt.append(el("div", { className: "traj-pt-score", text: h.score != null ? `${h.score}/10` : "?" })); pt.append(el("div", { className: "traj-pt-conf", text: `${h.conf_avg ?? "-"}%` })); pt.append(el("div", { className: "traj-pt-label", text: `iter${i+1}` })); line.append(pt); if (i < runs.length - 1) line.append(el("div", { className: "traj-spark-arrow", text: "→" })); }); card.append(line); // delta summary if (runs.length >= 2) { const first = runs[0], last = runs[runs.length - 1]; const dScore = (last.score != null && first.score != null) ? (last.score - first.score) : null; const dConf = (last.conf_avg != null && first.conf_avg != null) ? (last.conf_avg - first.conf_avg) : null; const delta = el("div", { className: "traj-spark-delta" }); if (dScore != null) delta.append(el("span", { text: `Δscore ${dScore > 0 ? "+" : ""}${dScore.toFixed(1)}`, className: dScore < 0 ? "delta-down" : dScore > 0 ? "delta-up" : "" })); if (dConf != null) delta.append(el("span", { text: ` · Δconf ${dConf > 0 ? "+" : ""}${dConf}%`, className: dConf > 0 ? "delta-up" : dConf < 0 ? "delta-down" : "" })); card.append(delta); } } card.addEventListener("click", () => { state.selected = { type: "file", id: `/home/profit/lakehouse/${s.file}` }; renderContext(); document.querySelector('#views button[data-view="trace"]').click(); }); grid.append(card); } body.append(grid); } } // ───── METRICS ───── function metricBox(label, big, kind, opts = {}) { // opts: { source, good, explain } // source = where the number comes from (data path) // good = the "what's a healthy value" sentence // explain = one-line definition of what this counts const box = el("div", { className: "metric" + (kind ? " " + kind : "") }); box.append(el("div", { className: "m-label", text: label })); box.append(el("div", { className: "m-big", text: big })); if (opts.explain) box.append(el("div", { className: "m-sub m-explain", text: opts.explain })); if (opts.source) box.append(el("div", { className: "m-sub m-source", text: "SOURCE · " + opts.source })); if (opts.good) box.append(el("div", { className: "m-sub m-good", text: "GOOD · " + opts.good })); return box; } function drawMetrics() { const grid = document.getElementById("metric-grid"); clear(grid); const byTier = { auto:0, dry_run:0, simulation:0, block:0, unknown:0 }; state.reviews.forEach(r => { const t = r.gradient_tier ?? "unknown"; if (byTier[t] != null) byTier[t]++; }); const total = state.reviews.length || 1; const confRows = state.reviews.filter(r => r.confidence_avg != null); const avg = confRows.length ? Math.round(confRows.reduce((a,r)=>a+r.confidence_avg,0)/confRows.length) : 0; const verdictCount = { pass:0, needs_patch:0, fail:0, unknown:0 }; state.reviews.forEach(r => { const v=r.verdict??"unknown"; if(verdictCount[v]!=null) verdictCount[v]++; }); const findingsTotal = state.reviews.reduce((a,r)=>a+(r.findings_count??0),0); const critTotal = state.reviews.reduce((a,r)=>a+(r.critical_failures_count??0),0); const verTotal = state.reviews.reduce((a,r)=>a+(r.verified_components_count??0),0); const usage = state.services?.subsystems?.find(n=>n.id==="usage")?.stats ?? {}; const journal = state.services?.subsystems?.find(n=>n.id==="journal")?.stats ?? {}; grid.append(metricBox("avg confidence", `${avg}%`, avg>=85?"good":avg>=70?"warn":"bad", { explain: "Self-assessed probability per suggestion, averaged across every review.", source: "scrum_reviews.jsonl .confidence_avg", good: "≥85% — model is confident. 70-84% routine. <70% means the scrum is uncertain and findings need human review.", })); grid.append(metricBox("scrum reviews", String(state.reviews.length), "good", { explain: "Every source file reviewed by the scrum master, across all iterations.", source: `${state.metrics.length} scrum runs tracked in scrum_loop_metrics.jsonl`, good: "Grows every run — 21 files × N iterations. Stall = pipeline broken.", })); grid.append(metricBox("critical failures", String(critTotal), critTotal>50?"bad":critTotal>10?"warn":"good", { explain: "Hard FAILs flagged by the forensic reviewer — pseudocode, fake implementations, unwired invariants. Each one is a concrete code-level gap.", source: "scrum_reviews.jsonl .critical_failures_count (forensic JSON format only)", good: "Trending DOWN each iteration = fixes are landing. Rising = new gaps surfacing faster than we close them.", })); grid.append(metricBox("verified components", String(verTotal), verTotal>0?"good":"warn", { explain: "What the scrum CONFIRMED is working — with file/line evidence. The inverse of critical_failures.", source: "scrum_reviews.jsonl .verified_components_count", good: "Trending UP = the system has more provably-real parts over time. Should grow as fixes land.", })); grid.append(metricBox("findings captured", String(findingsTotal), "good", { explain: "Total individual suggestions the scrum produced across all reviews (tables + JSON).", source: "scrum_reviews.jsonl .findings_count summed", good: "Higher = more scrutiny per file. Per-file average ≥10 means the review is substantive.", })); grid.append(metricBox("journal events", String(journal.total_events_created ?? 0), "good", { explain: "Mutation events recorded via ADR-012 append-only journal. Every ingest/delta-write should emit one.", source: "/journal/stats → total_events_created", good: "Should grow with ingest traffic. 1 = only a test probe fired; internal callers still unwired on most paths (P9-001).", })); grid.append(metricBox("v1 requests", String(usage.requests ?? 0), "good", { explain: "Calls through the Universal API /v1/chat endpoint (Phase 38). Captures all scrum + audit traffic.", source: `/v1/usage → requests. ${(usage.total_tokens ?? 0).toLocaleString()} tokens total`, good: "Every iteration adds ~21 requests. Stall = scrum paused OR callers bypassing the gateway (P44-style bypass).", })); // gradient bar const gb = el("div", { className: "metric" }); gb.append(el("div", { className: "m-label", text: "permission gradient" })); gb.append(el("div", { className: "m-big", text: String(state.reviews.length) })); gb.append(el("div", { className: "m-sub m-explain", text: "Tiers the scrum's suggestions by confidence: how much auto-apply we can trust per file." })); const bar = el("div", { className: "bar" }); bar.append(el("span", { className: "seg-auto", style: { width: `${100*byTier.auto/total}%` } })); bar.append(el("span", { className: "seg-dry_run", style: { width: `${100*byTier.dry_run/total}%` } })); bar.append(el("span", { className: "seg-simulation", style: { width: `${100*byTier.simulation/total}%` } })); bar.append(el("span", { className: "seg-block", style: { width: `${100*byTier.block/total}%` } })); gb.append(bar); gb.append(el("div", { className: "m-sub", text: `auto ${byTier.auto} · dry_run ${byTier.dry_run} · sim ${byTier.simulation} · block ${byTier.block}` })); gb.append(el("div", { className: "m-sub m-good", text: "AUTO (≥90%): ship the suggestion. DRY_RUN (70-89): apply then diff. SIMULATION (50-69): test first. BLOCK (<50): human review — the model doesn't trust itself." })); grid.append(gb); const vb = el("div", { className: "metric" }); vb.append(el("div", { className: "m-label", text: "verdict distribution" })); vb.append(el("div", { className: "m-big", text: String(verdictCount.pass + verdictCount.needs_patch + verdictCount.fail) })); vb.append(el("div", { className: "m-sub m-explain", text: "Forensic audit verdict per file: pass = works, needs_patch = fixable gaps, fail = not trustable." })); vb.append(el("div", { className: "m-sub", text: `pass ${verdictCount.pass} · needs_patch ${verdictCount.needs_patch} · fail ${verdictCount.fail}` })); vb.append(el("div", { className: "m-sub m-source", text: "SOURCE · scrum_reviews.jsonl .verdict (forensic JSON only — markdown rows count as unknown)" })); grid.append(vb); } // ───── KB ───── function drawKB() { const grid = document.getElementById("kb-grid"); clear(grid); // Explanatory banner — each iteration the scrum re-reviews every // target file and writes a row here. A card = one file's latest // review. Click to drill into its trace across all iterations. const banner = el("div", { className: "kb-banner" }); banner.append(el("div", { className: "kb-banner-title", text: "KNOWLEDGE BASE — every source file reviewed by the scrum master" })); banner.append(el("div", { className: "kb-banner-body", text: "Each card below is the LATEST scrum review of one source file. The review itself lives in data/_kb/scrum_reviews.jsonl. " + "Fields: score (scrum's alignment rating, 1-10 vs PRD intent), conf (model's self-assessed confidence per suggestion, avg'd), " + "findings (# of suggestions), crit (critical_failures — hard FAILs found), verified (verified_components — what's confirmed working). " + "Pills show: permission gradient (can we trust auto-apply), verdict (pass/needs_patch/fail), output format (JSON = forensic, markdown = legacy). " + "Click a card to see its trace across all iterations (iter 1 → iter N) and watch scores trend." })); grid.append(banner); const byFile = new Map(); state.reviews.forEach(r => { if (r.file) byFile.set(r.file, r); }); const rows = [...byFile.values()].sort((a,b) => (b.confidence_avg??0) - (a.confidence_avg??0)); // Quick stats above the cards const statLine = el("div", { className: "kb-statline" }); const avgConf = rows.length ? Math.round(rows.reduce((a,r)=>a+(r.confidence_avg??0),0) / rows.length) : 0; const scoreMean = rows.filter(r=>r.alignment_score!=null); const avgScore = scoreMean.length ? (scoreMean.reduce((a,r)=>a+r.alignment_score,0) / scoreMean.length).toFixed(1) : "?"; const blockCount = rows.filter(r => r.gradient_tier === "block").length; statLine.append(el("span", { text: `${rows.length} files tracked` })); statLine.append(el("span", { text: `mean score ${avgScore}/10` })); statLine.append(el("span", { text: `mean confidence ${avgConf}%` })); statLine.append(el("span", { text: `${blockCount} blocked (need human review)`, className: blockCount > 0 ? "stat-warn" : "" })); grid.append(statLine); rows.forEach(r => { const card = el("div", { className: "kb-file", data: { file: r.file } }); card.append(el("div", { className: "kf-path", text: r.file })); const meta = el("div", { className: "kf-meta" }); const scoreSpan = el("span", { className: "kf-score", text: `${r.alignment_score ?? "?"}/10` }); scoreSpan.title = "Scrum's alignment score (1-10) — how well this file matches PRD intent. Lower = more gaps."; meta.append(scoreSpan); const confSpan = el("span", { text: `conf ${r.confidence_avg ?? "-"}%` }); confSpan.title = "Average self-confidence across suggestions. <70% = model uncertain, treat carefully."; meta.append(confSpan); const findingsSpan = el("span", { text: `${r.findings_count ?? 0} findings` }); findingsSpan.title = "Total suggestions in this review (table rows or JSON array entries)."; meta.append(findingsSpan); const critSpan = el("span", { text: `${r.critical_failures_count ?? 0} crit` }); critSpan.title = "Critical failures: pseudocode, fake implementations, unwired invariants. Hard FAILs."; if ((r.critical_failures_count ?? 0) > 0) critSpan.style.color = "var(--red)"; meta.append(critSpan); const verSpan = el("span", { text: `${r.verified_components_count ?? 0} verified` }); verSpan.title = "Verified components: things the scrum CONFIRMED work, with file/line evidence."; if ((r.verified_components_count ?? 0) > 0) verSpan.style.color = "var(--green)"; meta.append(verSpan); meta.append(el("span", { text: (r.accepted_model ?? "").split("/").pop(), attrs: { title: "Which model produced this review" } })); card.append(meta); const pills = el("div", { className: "kf-meta" }); if (r.gradient_tier) { const p = el("span", { className: `pill tier-${r.gradient_tier}`, text: r.gradient_tier }); p.title = ({ auto: "AUTO — confidence ≥90%, suggestions safe to apply automatically", dry_run: "DRY_RUN — confidence 70-89%, apply then review the diff", simulation: "SIMULATION — confidence 50-69%, test in sandbox first", block: "BLOCK — confidence <50%, requires human review, do not auto-apply", })[r.gradient_tier] ?? r.gradient_tier; pills.append(p); } if (r.verdict) { const p = el("span", { className: `pill ver-${r.verdict}`, text: r.verdict }); p.title = ({ pass: "PASS — scrum confirms this file meets its PRD intent", needs_patch: "NEEDS_PATCH — gaps exist but are fixable; scrum has concrete suggestions", fail: "FAIL — file cannot be trusted for its claimed purpose without structural changes", })[r.verdict] ?? r.verdict; pills.append(p); } if (r.output_format) { const p = el("span", { className: `pill fmt-${r.output_format}`, text: r.output_format }); p.title = r.output_format === "forensic_json" ? "FORENSIC_JSON — structured output with verdict/critical/verified/missing fields. Richer signal." : "MARKDOWN — legacy tabular output. Lower structure; we only extract confidence scalars from these."; pills.append(p); } card.append(pills); card.addEventListener("click", () => { state.selected = { type: "file", id: r.file }; renderContext(); document.querySelector('#views button[data-view="trace"]').click(); }); grid.append(card); }); } // ───── CONSOLE ───── // Persistent selection across polls so tab switches survive. state.consoleSvc = "gateway"; // Hook tab buttons once document.querySelectorAll("#con-tabs button").forEach(b => { b.addEventListener("click", () => { document.querySelectorAll("#con-tabs button").forEach(x => x.classList.remove("on")); b.classList.add("on"); state.consoleSvc = b.dataset.svc; drawConsole(); }); }); async function drawConsole() { const log = document.getElementById("console-log"); clear(log); const unit = document.getElementById("con-unit"); if (unit) unit.textContent = ""; if (state.consoleSvc === "summary") { drawConsoleSummary(log); return; } // Per-service log tail const svc = state.consoleSvc; try { const res = await fetch(`/data/logs/${svc}?n=120`).then(r => r.json()); if (unit && res.unit) unit.textContent = `unit · ${res.unit}`; if (res.error) { log.append(lineInfo(`[error] ${res.error}`, "cl-err")); return; } const lines = res.lines ?? []; if (!lines.length) { log.append(lineInfo("(no log lines — unit may have just started)", "cl-info")); return; } lines.forEach(l => { const cls = /\berror\b|\bERROR\b|panic|\[ERROR|failed/.test(l) ? "cl-err" : /\bwarn\b|\bWARN\b|\bwarning\b|\[WARN/.test(l) ? "cl-warn" : /\baccepted\b|\bok\b|\bOK\b|success|complete|ready/.test(l) ? "cl-ok" : "cl-info"; log.append(lineInfo(l, cls)); }); // autoscroll to bottom log.scrollTop = log.scrollHeight; } catch (e) { log.append(lineInfo(`[fetch-error] ${e}`, "cl-err")); } } function lineInfo(text, cls) { return el("div", { className: "cl-line " + cls, text }); } function drawConsoleSummary(log) { const info = t => lineInfo(t, "cl-info"); const ok = t => lineInfo(t, "cl-ok"); const warn = t => lineInfo(t, "cl-warn"); const err = t => lineInfo(t, "cl-err"); log.append(info(`# Lakehouse VCP · ${new Date().toLocaleTimeString()}`)); log.append(info(`# Services`)); for (const n of state.services?.nodes ?? []) { const line = `[${String(n.status).padEnd(8)}] ${n.label}`; log.append(n.status === "healthy" ? ok(line) : n.status === "down" ? err(line) : warn(line)); } log.append(info(`# Subsystems`)); for (const s of state.services?.subsystems ?? []) { log.append(info(` ${String(s.id).padEnd(10)} ${JSON.stringify(s.stats ?? {}).slice(0, 120)}`)); } log.append(info(`# Recent overrides (layer 10)`)); for (const o of state.overrides.slice(-6)) { log.append(warn(` [${o.ts}] ${o.task_signature}: ${o.human_fix}`)); } log.append(info(`# Model trust accumulated`)); const agg = {}; for (const t of state.trust) { const k = t.accepted_model ?? "?"; agg[k] = agg[k] ?? { accepts:0, thin:0, attempts:0 }; agg[k].accepts++; agg[k].thin += t.thin_rejections ?? 0; agg[k].attempts += t.attempts_made ?? 0; } for (const [m, s] of Object.entries(agg)) { log.append(info(` ${String(m).padEnd(48)} accepts=${s.accepts} thin=${s.thin} attempts=${s.attempts}`)); } } // ───── boot ───── poll(); setInterval(poll, POLL_MS); window.addEventListener("resize", () => { if (state.services && state.view === "map") drawMap(state.services); });