[object Object]

← back to Agent Cabinet

Build out both proposals LIVE: đŸ©ș Health Overlay (reads 6 DW canary latest.json, maps verdicts→officers, tints nodes + status dots) + 💾 Cost Heatmap (reads cost-ledger.jsonl, app→officer spend, ranks top spenders, node badges); /api/health + /api/cost; clear-overlay control

9634ba240e97c177cf30f105283fba5f66bf5c78 · 2026-06-16 16:45:36 -0700 · SteveStudio2

Files touched

Diff

commit 9634ba240e97c177cf30f105283fba5f66bf5c78
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Tue Jun 16 16:45:36 2026 -0700

    Build out both proposals LIVE: đŸ©ș Health Overlay (reads 6 DW canary latest.json, maps verdicts→officers, tints nodes + status dots) + 💾 Cost Heatmap (reads cost-ledger.jsonl, app→officer spend, ranks top spenders, node badges); /api/health + /api/cost; clear-overlay control
---
 pyramid.html | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 server.js    | 53 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 124 insertions(+), 4 deletions(-)

diff --git a/pyramid.html b/pyramid.html
index 93bd843..7cba1c9 100644
--- a/pyramid.html
+++ b/pyramid.html
@@ -168,6 +168,22 @@
   .m-list{font-size:12px}
   .m-list .row{display:flex;justify-content:space-between;gap:10px;padding:6px 2px;border-bottom:1px solid #141b28}
   .m-quote{font-style:italic;color:#9fb0c9;border-left:3px solid #6d34f4;padding:6px 12px;margin:10px 0;font-size:12px;background:rgba(109,52,244,.07)}
+  /* health + cost overlays */
+  .officer-node.h-up{border-color:#16a34a !important}
+  .officer-node.h-warn{border-color:#fbbf24 !important}
+  .officer-node.h-down{border-color:#ef4444 !important}
+  .health-dot{position:absolute;top:7px;right:8px;width:11px;height:11px;border-radius:50%;box-shadow:0 0 0 2px #0b0e14}
+  .health-dot.up{background:#34d399}.health-dot.warn{background:#fbbf24}.health-dot.down{background:#ef4444}.health-dot.na{background:#4b5563}
+  .cost-badge{position:absolute;top:7px;left:8px;font:800 9.5px ui-monospace,monospace;color:#fde68a;background:#3a2a05;border:1px solid #b8860b;border-radius:5px;padding:1px 5px}
+  .cost-bar{position:absolute;left:0;bottom:0;height:3px;background:linear-gradient(90deg,#f59e0b,#ef4444)}
+  #clear-overlay{position:fixed;bottom:20px;left:20px;z-index:140;background:#161d2c;border:1px solid #2b3650;color:#bcd0f0;border-radius:9px;padding:8px 14px;font-size:12px;cursor:pointer;display:none;box-shadow:0 6px 20px rgba(0,0,0,.5)}
+  #clear-overlay.show{display:block}
+  .hstat{display:inline-flex;align-items:center;gap:6px;font-size:12px;padding:3px 0}
+  .hstat .d{width:10px;height:10px;border-radius:50%}
+  .bar-row{display:flex;align-items:center;gap:8px;font-size:11.5px;margin:3px 0}
+  .bar-row .nm{width:170px;color:#cfd8e6;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
+  .bar-row .bar{height:9px;border-radius:5px;background:linear-gradient(90deg,#6d34f4,#a78bfa)}
+  .bar-row .v{color:#fde68a;font:11px ui-monospace,monospace;min-width:52px;text-align:right}
 </style>
 </head>
 <body>
@@ -219,6 +235,7 @@
 </div>
 <div id="toast"></div>
 <div id="modal" class="modal-overlay"><div class="modal-box"><button class="modal-x" onclick="closeModal()">✕</button><div id="modal-content" class="m-pad"></div></div></div>
+<button id="clear-overlay" onclick="clearOverlays()">⊘ Clear overlay</button>
 
 <script>
 const OFF = [
@@ -570,8 +587,8 @@ const IDEAS=[
   {icon:'🔎', title:'Task Router — “Who handles this?”', live:true, render:'router', pitch:'Type any task; route it to the right officer → agent → skill, with one-click Run.'},
   {icon:'🧬', title:'Consolidation Advisor', live:true, render:'consol', pitch:'Surface skills shared across officers and recommend folds — per the cabinet’s own architecture rules.'},
   {icon:'đŸ“€', title:'Org Snapshot Export', live:true, render:'export', pitch:'Download the whole cabinet as JSON + Markdown for docs, diffs and sharing.'},
-  {icon:'đŸ©ș', title:'Live Health Overlay', live:false, render:'health', pitch:'Tint each officer node by its domain’s current uptime / canary verdict — see what’s on fire at a glance.'},
-  {icon:'💾', title:'Cost / Usage Heatmap', live:false, render:'cost', pitch:'Size & tint nodes by invocation count or $ from the cost-ledger so the hot (and dead) agents pop.'},
+  {icon:'đŸ©ș', title:'Live Health Overlay', live:true, render:'health', pitch:'Tint each officer node by its domain’s current uptime / canary verdict — see what’s on fire at a glance.'},
+  {icon:'💾', title:'Cost / Usage Heatmap', live:true, render:'cost', pitch:'Tint nodes & rank apps by $ from the cost-ledger so the hot (and dead) spenders pop.'},
 ];
 function openIdeasGallery(){
   const cards=IDEAS.map((id,i)=>`<div class="idea-card" onclick="openIdea(${i})"><div class="ic">${id.icon}</div><div class="t">${id.title}</div><div class="p">${id.pitch}</div><span class="tag ${id.live?'live':'prop'}">${id.live?'● LIVE':'◆ PROPOSAL'}</span></div>`).join('');
@@ -583,10 +600,11 @@ function openIdea(i){
   if(id.render==='router') body=routerHTML();
   else if(id.render==='consol') body=consolHTML();
   else if(id.render==='export') body=exportHTML();
-  else if(id.render==='health') body=`<div class="m-sub">Proposal. The cabinet server would read each DW canary’s <code>data/latest.json</code> + the uptime probe and tint every officer node green / amber / red by its domain’s live verdict (Operations=fleet uptime, Commerce=scraper-canary, Compliance=leak-scan
). One glance tells you which department is on fire.</div><div class="m-quote">Maps the abstract org chart onto real-time fleet health.</div>`;
-  else if(id.render==='cost') body=`<div class="m-sub">Proposal. Pull <code>~/.claude/cost-ledger.jsonl</code> + invocation counts and scale each node’s size / heat by spend or usage — so expensive agents and never-run skills are obvious. Directly serves the architecture goal of retiring dead skills.</div><div class="m-quote">Turns the pyramid into a spend &amp; usage heatmap.</div>`;
+  else body='<div id="idea-dyn" class="m-sub">Loading live data
</div>';
   openModal(`<h2 class="m-h">${id.icon} ${id.title}</h2><div class="m-sub">${id.pitch}</div>${body}${ideaFooter(i)}`);
   if(id.render==='router'){ const inp=document.getElementById('route-q'); if(inp){ inp.focus(); inp.addEventListener('keydown',e=>{ if(e.key==='Enter') routeTask(); }); } }
+  else if(id.render==='health') loadHealth();
+  else if(id.render==='cost') loadCost();
 }
 function queueIdea(i){ const id=IDEAS[i];
   fetch('/api/queue-idea?title='+encodeURIComponent('[Cabinet] '+id.title)+'&summary='+encodeURIComponent(id.pitch)).then(r=>r.json())
@@ -620,6 +638,55 @@ function buildExport(){
 function exportHTML(){ return `<div class="m-sub">Download the live cabinet structure (7 officers, all agents/skills, the ${SHARED.length} cross-officer shares + ${SUGGEST.length} suggestions).</div><div class="m-actions" style="border:none;padding:0;margin-top:6px"><button class="m-btn primary" onclick="dl('json')">⬇ cabinet.json</button><button class="m-btn primary" onclick="dl('md')">⬇ cabinet.md</button></div>`; }
 function dl(kind){ const e=buildExport(); const blob=new Blob([kind==='json'?e.json:e.md],{type:'text/plain'}); const a=document.createElement('a'); a.href=URL.createObjectURL(blob); a.download='cabinet.'+(kind==='json'?'json':'md'); a.click(); toast('⬇ cabinet.'+kind+' downloaded'); }
 
+// Idea 4 — Live Health Overlay (real DW canary verdicts)
+let HEALTH=null, COST=null, _overlay=null;
+const HCOL={up:'#34d399',warn:'#fbbf24',down:'#ef4444',na:'#4b5563'};
+async function loadHealth(){
+  const box=document.getElementById('idea-dyn'); if(!box) return;
+  let d; try{ d=await(await fetch('/api/health')).json(); }catch(e){ box.innerHTML='<div class="dr-note">server unreachable — open via :9766/pyramid</div>'; return; }
+  HEALTH=d;
+  let h='<div style="margin:2px 0 12px">';
+  OFF.forEach(o=>{ const oi=d.officers[o.id]; const st=oi?oi.status:'na';
+    h+=`<div class="hstat"><span class="d" style="background:${HCOL[st]}"></span><b style="color:${o.col}">${o.nm.replace('VP ','')}</b> — <b>${st.toUpperCase()}</b>${oi?'<div style="color:#8a94a6;font-size:10.5px;margin:1px 0 4px 16px">'+oi.detail+'</div>':' <span style="color:#6b7280;font-size:10.5px">(no monitor)</span>'}</div>`; });
+  h+='</div><div class="m-actions" style="border:none;padding:0;margin:0"><button class="m-btn primary" onclick="applyHealth()">đŸ©ș Apply to pyramid</button><button class="m-btn ghost" onclick="clearOverlays()">Clear</button></div>';
+  box.innerHTML=h;
+}
+function applyHealth(){
+  if(!HEALTH) return; clearOverlays(true);
+  OFF.forEach(o=>{ if(!o._node) return; const oi=HEALTH.officers[o.id]; const st=oi?oi.status:'na';
+    o._node.classList.add('h-'+st);
+    const dot=document.createElement('span'); dot.className='health-dot '+st; dot.title=oi?oi.detail:'no monitor'; o._node.appendChild(dot); });
+  _overlay='health'; document.getElementById('clear-overlay').classList.add('show'); closeModal(); toast('đŸ©ș health overlay applied');
+}
+// Idea 5 — Cost / Usage Heatmap (real cost-ledger spend, app→officer mapped)
+function appOfficer(app){ for(const o of OFF){ if(o.agents.includes(app)||o.skills.includes(app)||(o.scrapers||[]).includes(app)) return o.id; } return null; }
+async function loadCost(){
+  const box=document.getElementById('idea-dyn'); if(!box) return;
+  let d; try{ d=await(await fetch('/api/cost')).json(); }catch(e){ box.innerHTML='<div class="dr-note">server unreachable</div>'; return; }
+  if(!d.ok){ box.innerHTML='<div class="dr-note">'+(d.error||'no data')+'</div>'; return; }
+  COST=d;
+  const max=Math.max(...d.by_app.slice(0,10).map(a=>a.usd),0.0001);
+  let h=`<div class="m-sub" style="margin:2px 0 10px">Total <b style="color:#fde68a">$${d.total_usd}</b> over ${d.entries.toLocaleString()} calls · ${(d.first||'').slice(0,10)} → ${(d.last||'').slice(0,10)}</div><div style="font-size:11px;color:#8a94a6;margin-bottom:5px">Top spenders by app (▾ mapped to officer where it’s a cabinet skill)</div>`;
+  d.by_app.slice(0,10).forEach(a=>{ const off=appOfficer(a.name);
+    h+=`<div class="bar-row"><span class="nm" title="${a.name}">${a.name}${off?' <span style="color:#8a94a6">▾ '+offName(off)+'</span>':''}</span><span class="bar" style="width:${Math.max(4,a.usd/max*220)}px"></span><span class="v">$${a.usd.toFixed(2)}</span></div>`; });
+  h+='<div class="m-actions" style="border:none;padding:0;margin-top:12px"><button class="m-btn primary" onclick="applyCost()">💾 Apply heatmap</button><button class="m-btn ghost" onclick="clearOverlays()">Clear</button></div>';
+  box.innerHTML=h;
+}
+function applyCost(){
+  if(!COST) return; clearOverlays(true);
+  const byOff={}; COST.by_app.forEach(a=>{ const off=appOfficer(a.name); if(off) byOff[off]=(byOff[off]||0)+a.usd; });
+  const max=Math.max(...Object.values(byOff),0.0001);
+  OFF.forEach(o=>{ if(!o._node) return; const usd=byOff[o.id]||0;
+    const badge=document.createElement('span'); badge.className='cost-badge'; badge.textContent='$'+usd.toFixed(2); o._node.appendChild(badge);
+    const bar=document.createElement('span'); bar.className='cost-bar'; bar.style.width=(usd>0?Math.max(8,usd/max*100):0)+'%'; o._node.appendChild(bar); });
+  _overlay='cost'; document.getElementById('clear-overlay').classList.add('show'); closeModal(); toast('💾 cost heatmap applied (officer-mapped spend)');
+}
+function clearOverlays(silent){
+  document.querySelectorAll('.officer-node').forEach(n=>{ n.classList.remove('h-up','h-warn','h-down'); n.querySelectorAll('.health-dot,.cost-badge,.cost-bar').forEach(e=>e.remove()); });
+  document.getElementById('clear-overlay').classList.remove('show'); _overlay=null;
+  if(!silent) toast('overlay cleared');
+}
+
 legend(); build(); timeline(); buildToolbar();
 document.getElementById('pyramid').addEventListener('click', onNodeClick);
 document.getElementById('xlines').addEventListener('click', e=>{ const t=e.target.closest('.sug-hit'); if(t) openSuggestion(t.dataset.key); });
diff --git a/server.js b/server.js
index 4b3bc28..0e41232 100644
--- a/server.js
+++ b/server.js
@@ -110,6 +110,31 @@ function gitCommitCabinet(s, cb) {
   });
 }
 
+// ── Idea 4: Live Health Overlay — read DW canary verdicts, map to officers ──
+const HEALTH_SOURCES = {
+  'vp-operations':        ['dw-uptime-probe', 'dw-backup-canary', 'dw-canary-meta-watchdog'],
+  'vp-dw-commerce':       ['dw-scraper-canary', 'dw-map-auditor'],
+  'vp-compliance-policy': ['dw-leak-scanner'],
+};
+function readCanary(skill) {
+  try {
+    const j = JSON.parse(fs.readFileSync(path.join(HOME, '.claude', 'skills', skill, 'data', 'latest.json'), 'utf8'));
+    const ts = j.scanned_at || j.checked_at || null;
+    const down = (j.down || 0) + (j.leaking || 0);
+    const fail = (j.fail || 0);
+    const warn = (j.warn || 0) + (j.degraded || 0) + (j.unknown || 0);
+    const sev = (down > 0 || fail > 0) ? 'FAIL' : (warn > 0 ? 'WARN' : 'PASS');
+    const bits = [];
+    if (j.up != null) bits.push(j.up + ' up'); if (j.down) bits.push(j.down + ' down'); if (j.degraded) bits.push(j.degraded + ' degraded');
+    if (j.vendors != null) bits.push(j.vendors + ' vendors'); if (j.fail) bits.push(j.fail + ' fail'); if (j.warn) bits.push(j.warn + ' warn');
+    if (j.unknown) bits.push(j.unknown + ' unknown'); if (j.leaking != null) bits.push((j.leaking || 0) + ' leaking');
+    if (j.repos != null) bits.push(j.repos + ' repos'); if (j.canaries != null) bits.push(j.canaries + ' canaries');
+    const ageH = ts ? +((Date.now() - Date.parse(ts)) / 3.6e6).toFixed(1) : null;
+    return { skill, sev, ts, ageH, summary: bits.join(' · ') };
+  } catch (e) { return { skill, sev: 'NA', summary: 'no data' }; }
+}
+const officerStatus = (sev) => sev === 'FAIL' ? 'down' : sev === 'WARN' ? 'warn' : sev === 'PASS' ? 'up' : 'na';
+
 function parseYaml(text) {
   // Tiny YAML subset parser — good enough for cabinet.yaml's shape.
   // Produces {president, cabinet:[{vp, domain, triggers:[], directors:[{skill?,subagent?,owns?}]}]}
@@ -631,6 +656,34 @@ const server = http.createServer((req, res) => {
     cr.write(payload); cr.end();
     return;
   }
+  if (u.pathname === '/api/health') {
+    const canaries = {}, officers = {}, rank = { down: 3, warn: 2, up: 1, na: 0 };
+    for (const off in HEALTH_SOURCES) {
+      let worst = 'na'; const details = [];
+      HEALTH_SOURCES[off].forEach(sk => {
+        const c = readCanary(sk); canaries[sk] = c;
+        const st = officerStatus(c.sev); if (rank[st] > rank[worst]) worst = st;
+        details.push(sk + ': ' + c.sev + (c.summary ? ' (' + c.summary + ')' : ''));
+      });
+      officers[off] = { status: worst, detail: details.join('  ·  ') };
+    }
+    return J({ ok: true, generated_at: new Date().toISOString(), officers, canaries });
+  }
+  if (u.pathname === '/api/cost') {
+    let total = 0, n = 0, first = null, last = null; const byApp = {}, byVendor = {};
+    try {
+      const lines = fs.readFileSync(path.join(HOME, '.claude', 'cost-ledger.jsonl'), 'utf8').split('\n');
+      for (const ln of lines) {
+        if (!ln.trim()) continue; let j; try { j = JSON.parse(ln); } catch (e) { continue; }
+        const c = +j.cost_usd || 0; total += c; n++;
+        byApp[j.app || '?'] = (byApp[j.app || '?'] || 0) + c;
+        byVendor[j.vendor || '?'] = (byVendor[j.vendor || '?'] || 0) + c;
+        if (j.ts) { if (!first || j.ts < first) first = j.ts; if (!last || j.ts > last) last = j.ts; }
+      }
+    } catch (e) { return J({ ok: false, error: 'no cost-ledger' }); }
+    const sortObj = (o) => Object.entries(o).map(([k, v]) => ({ name: k, usd: +v.toFixed(4) })).sort((a, b) => b.usd - a.usd);
+    return J({ ok: true, total_usd: +total.toFixed(2), entries: n, first, last, by_app: sortObj(byApp).slice(0, 80), by_vendor: sortObj(byVendor).slice(0, 20) });
+  }
 
   if (req.url === '/cabinet.yaml') {
     try {

← 52c593f Pyramid: 💡 Ideas modals — 5 upgrade ideas as modals (Task R  Â·  back to Agent Cabinet  Â·  Add /activity viewer — live date/time timeline aggregating g 101c9bc →