← back to Agent Cabinet
Pyramid: cross-officer lines — 18 'shared now' (computed from cabinet duplicates) + 12 curated 'suggested moves' arrows, toggleable + analysis list drawer
eb43caf11a8b9aecd80b348283dd60d71c1a5866 · 2026-06-16 15:35:18 -0700 · SteveStudio2
Files touched
Diff
commit eb43caf11a8b9aecd80b348283dd60d71c1a5866
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue Jun 16 15:35:18 2026 -0700
Pyramid: cross-officer lines — 18 'shared now' (computed from cabinet duplicates) + 12 curated 'suggested moves' arrows, toggleable + analysis list drawer
---
pyramid.html | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 99 insertions(+), 6 deletions(-)
diff --git a/pyramid.html b/pyramid.html
index d89213d..6bfe4be 100644
--- a/pyramid.html
+++ b/pyramid.html
@@ -22,6 +22,13 @@
.scroller{overflow:auto;padding:26px 24px 40px}
#pyramid{position:relative;min-width:1180px}
svg#lines{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible;z-index:1}
+ svg#xlines{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible;z-index:1}
+ .toolbar{display:flex;gap:16px;align-items:center;margin-top:11px;flex-wrap:wrap}
+ .toolbar label{display:flex;align-items:center;gap:6px;font-size:12px;color:#aeb8c8;cursor:pointer;user-select:none}
+ .toolbar input{accent-color:#6d34f4;cursor:pointer}
+ .tog-line{display:inline-block;width:22px;border-top-width:2px;height:0;vertical-align:middle}
+ .tb-btn{flex:none;background:#161d2c;border:1px solid #2b3650;color:#bcd0f0;border-radius:7px;padding:5px 12px;font-size:12px;cursor:pointer;margin-left:auto}
+ .tb-btn:hover{background:#1b2435}
.tier{position:relative;z-index:2}
.tier-label{position:absolute;left:-2px;transform:translateX(-100%);color:var(--dim);font-size:10px;letter-spacing:.12em;text-transform:uppercase;white-space:nowrap;padding-right:14px;opacity:.65}
@@ -119,11 +126,13 @@
<header>
<h1>The Cabinet <small>President → Officers → Agents → Skills → Workflows · live structure from cabinet.yaml</small></h1>
<div class="legend" id="legend"></div>
+ <div class="toolbar" id="toolbar"></div>
</header>
<div class="scroller">
<div id="pyramid">
<svg id="lines"></svg>
+ <svg id="xlines"></svg>
<div class="tier president"><div class="tier-label" style="top:14px">President</div>
<div class="pres-node" id="pres"><div class="t">Steve</div><div class="s">Chief Executive · steve@designerwallcoverings.com</div></div>
</div>
@@ -223,6 +232,26 @@ const JOBS = [
];
const childRefs = []; // {childEl, parentEl, kind}
+const allNodes = []; // {el, kind, id, officer}
+let SHARED = [];
+
+// Curated cross-officer SUGGESTIONS — a task that should ALSO live under another
+// officer (drawn as amber arrows). Distinct from "shared now" which is computed
+// from cabinet.yaml duplicates. {id, kind, from, to, reason}
+const SUGGEST = [
+ { id:'dw-legal-compliance', kind:'skill', from:'vp-dw-commerce', to:'vp-compliance-policy', reason:'Settlement / vendor-contract review is also a compliance gate' },
+ { id:'security-auditor', kind:'subagent', from:'vp-engineering', to:'vp-compliance-policy', reason:'Security posture (OWASP/auth) overlaps policy & compliance' },
+ { id:'best-practices', kind:'skill', from:'vp-compliance-policy',to:'vp-engineering', reason:'Standing-rules pre-flight before any code / prod op' },
+ { id:'la-research-agent', kind:'subagent', from:'vp-directories', to:'vp-research-content', reason:'LA public-records work IS a core research capability' },
+ { id:'analytics-agent', kind:'subagent', from:'vp-operations', to:'vp-research-content', reason:'GA4 traffic data feeds site audits & competitor analysis' },
+ { id:'comms-compliance', kind:'subagent', from:'vp-compliance-policy',to:'vp-dw-marketing', reason:'Every mailer / social send must clear CAN-SPAM first' },
+ { id:'dw-vendor-landing', kind:'skill', from:'vp-dw-marketing', to:'vp-dw-commerce', reason:'Vendor landings are storefront surfaces commerce owns' },
+ { id:'collection-creator', kind:'skill', from:'vp-dw-marketing', to:'vp-dw-commerce', reason:'Collection/merchandising lives in the Shopify catalog' },
+ { id:'new-arrivals-rotator',kind:'skill', from:'vp-dw-marketing', to:'vp-dw-commerce', reason:'Auto-rotating Shopify collections are catalog ops' },
+ { id:'pairs-well-with', kind:'skill', from:'vp-dw-marketing', to:'vp-dw-commerce', reason:'Per-SKU recommendations are a product-page feature' },
+ { id:'exa-agent', kind:'subagent', from:'vp-research-content', to:'vp-directories', reason:'Web research enriches directory listings' },
+ { id:'logo-agent', kind:'skill', from:'vp-dw-marketing', to:'vp-research-content', reason:'Tournament brand-mark picker is a design/research tool' },
+];
function legend(){
const L = document.getElementById('legend');
@@ -247,18 +276,20 @@ function build(){
const sc = o.scrapers?o.scrapers.length:0;
node.innerHTML = `<div class="nm">${o.nm}</div><div class="dm">${o.dm}</div>
<div class="ct">${o.agents.length} agents · ${o.skills.length+sc} skills</div>`;
- col.appendChild(node); o._node=node;
+ col.appendChild(node); o._node=node; allNodes.push({el:node,kind:'officer',id:o.id,officer:o.id});
const aL=document.createElement('div'); aL.className='group-label'; aL.textContent='Agents'; col.appendChild(aL);
const aWrap=document.createElement('div'); aWrap.className='agents';
o.agents.forEach(a=>{ const e=document.createElement('div'); e.className='agent'; e.style.setProperty('--oc',o.col); e.textContent=a;
- e.dataset.kind='subagent'; e.dataset.id=a; e.dataset.label=a; aWrap.appendChild(e); childRefs.push({c:e,p:node,kind:'agent',col:o.col}); });
+ e.dataset.kind='subagent'; e.dataset.id=a; e.dataset.label=a; aWrap.appendChild(e); childRefs.push({c:e,p:node,kind:'agent',col:o.col});
+ allNodes.push({el:e,kind:'agent',id:a,officer:o.id}); });
col.appendChild(aWrap);
const sL=document.createElement('div'); sL.className='group-label'; sL.textContent='Skills'; col.appendChild(sL);
const sWrap=document.createElement('div'); sWrap.className='skills';
o.skills.forEach(s=>{ const e=document.createElement('div'); e.className='skill'; e.textContent=s;
- e.dataset.kind='skill'; e.dataset.id=s; e.dataset.label=s; sWrap.appendChild(e); childRefs.push({c:e,p:node,kind:'skill',col:o.col}); });
+ e.dataset.kind='skill'; e.dataset.id=s; e.dataset.label=s; sWrap.appendChild(e); childRefs.push({c:e,p:node,kind:'skill',col:o.col});
+ allNodes.push({el:e,kind:'skill',id:s,officer:o.id}); });
if(o.scrapers){ const e=document.createElement('div'); e.className='skill scr'; e.style.setProperty('--oc',o.col);
e.textContent='Vendor Scrapers ×'+o.scrapers.length; e.title='Click to open all '+o.scrapers.length+' scraper skills';
e.dataset.kind='scrapers'; e.dataset.scrapers=JSON.stringify(o.scrapers);
@@ -371,11 +402,73 @@ function onNodeClick(e){
openPanel(kind, el.dataset.id, el.dataset.label);
}
-legend(); build(); timeline();
+// ── Cross-officer analysis: SHARED-now (computed) + SUGGESTED (curated) ──
+function hashHue(s){ let h=0; for(let i=0;i<s.length;i++) h=(h*31+s.charCodeAt(i))>>>0; return h%360; }
+function midC(el,root){ const c=center(el,root); return { x:c.x, y:c.y + c.h/2 }; }
+function computeShared(){
+ const byId={};
+ allNodes.forEach(n=>{ if(n.kind!=='agent'&&n.kind!=='skill') return; (byId[n.id]=byId[n.id]||[]).push(n); });
+ const out=[];
+ Object.keys(byId).forEach(id=>{ const insts=byId[id]; const offs=[...new Set(insts.map(x=>x.officer))]; if(offs.length>1) out.push({id,insts,officers:offs}); });
+ return out.sort((a,b)=>b.officers.length-a.officers.length);
+}
+function drawCross(){
+ const root=document.getElementById('pyramid'), svg=document.getElementById('xlines');
+ svg.setAttribute('width',root.scrollWidth); svg.setAttribute('height',root.scrollHeight); svg.setAttribute('viewBox',`0 0 ${root.scrollWidth} ${root.scrollHeight}`);
+ let sh='', sg='';
+ SHARED.forEach(s=>{
+ const pts=s.insts.map(n=>midC(n.el,root)).sort((a,b)=>a.x-b.x);
+ const hue=hashHue(s.id);
+ let d=`M${pts[0].x},${pts[0].y}`;
+ for(let i=1;i<pts.length;i++){ const mx=(pts[i-1].x+pts[i].x)/2, top=Math.min(pts[i-1].y,pts[i].y)-46; d+=` Q${mx},${top} ${pts[i].x},${pts[i].y}`; }
+ sh+=`<path d="${d}" fill="none" stroke="hsl(${hue} 82% 66%)" stroke-width="1.4" stroke-opacity=".9" stroke-dasharray="5 4"/>`;
+ pts.forEach(p=>{ sh+=`<circle cx="${p.x}" cy="${p.y}" r="2.6" fill="hsl(${hue} 82% 66%)"/>`; });
+ });
+ SUGGEST.forEach(g=>{
+ const src=allNodes.find(n=>n.id===g.id && n.officer===g.from);
+ const dst=OFF.find(o=>o.id===g.to);
+ if(!src||!dst||!dst._node) return;
+ const a=midC(src.el,root), b=center(dst._node,root), bx=b.x, by=b.y, my=(a.y+by)/2;
+ sg+=`<path d="M${a.x},${a.y} C${a.x},${my} ${bx},${my} ${bx},${by}" fill="none" stroke="#fbbf24" stroke-width="1.3" stroke-opacity=".8" stroke-dasharray="1.5 4.5" marker-end="url(#arr)"/>`;
+ });
+ svg.innerHTML=`<defs><marker id="arr" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#fbbf24"/></marker></defs><g id="g-shared">${sh}</g><g id="g-suggest">${sg}</g>`;
+ applyToggles();
+}
+function applyToggles(){
+ const on=id=>{ const e=document.getElementById(id); return e?e.checked:true; };
+ document.getElementById('lines').style.display = on('tg-hier')?'':'none';
+ const gs=document.getElementById('g-shared'), gg=document.getElementById('g-suggest');
+ if(gs) gs.style.display = on('tg-shared')?'':'none';
+ if(gg) gg.style.display = on('tg-sug')?'':'none';
+}
+function buildToolbar(){
+ SHARED=computeShared();
+ document.getElementById('toolbar').innerHTML =
+ `<label><input type="checkbox" id="tg-hier" checked> Hierarchy</label>`+
+ `<label><input type="checkbox" id="tg-shared" checked><span class="tog-line" style="border-top:2px dashed #cbd5e1"></span>Shared now (${SHARED.length})</label>`+
+ `<label><input type="checkbox" id="tg-sug" checked><span class="tog-line" style="border-top:2px dotted #fbbf24"></span>Suggested moves (${SUGGEST.length})</label>`+
+ `<button class="tb-btn" onclick="showAnalysis()">▤ List shares & suggestions</button>`;
+ ['tg-hier','tg-shared','tg-sug'].forEach(id=>document.getElementById(id).addEventListener('change',applyToggles));
+}
+function offName(id){ const o=OFF.find(x=>x.id===id); return o?o.nm.replace('VP ',''):id; }
+function showAnalysis(){
+ const dr=document.getElementById('drawer'); dr.classList.add('open');
+ document.getElementById('dr-title').textContent='Cross-officer analysis';
+ const k=document.getElementById('dr-kind'); k.textContent='SHARES'; k.className='dr-kind k-agent';
+ document.getElementById('dr-run').style.display='none'; document.getElementById('dr-open').style.display='none';
+ let h=`<div class="dr-note"><b style="color:#cbd5e1">Shared now (${SHARED.length})</b> — already wired to ≥2 officers (dashed colored lines). A task serving many officers.</div>`;
+ h+=SHARED.map(s=>`<div class="scr-row"><span class="scr-n" style="color:hsl(${hashHue(s.id)} 82% 70%)">${s.id}</span><span style="font-size:10.5px;color:#8a94a6;text-align:right">${s.officers.map(offName).join(' · ')}</span></div>`).join('');
+ h+=`<div class="dr-note" style="margin-top:16px"><b style="color:#fbbf24">Suggested moves (${SUGGEST.length})</b> — should also belong to another officer (amber arrows).</div>`;
+ h+=SUGGEST.map(g=>`<div class="scr-row" style="display:block"><div><span class="scr-n">${g.kind==='skill'?'/':'@'}${g.id}</span> <span style="color:#fbbf24;font-size:10.5px">${offName(g.from)} → ${offName(g.to)}</span></div><div style="font-size:10.5px;color:#8a94a6;margin-top:3px;line-height:1.4">${g.reason}</div></div>`).join('');
+ document.getElementById('dr-body').innerHTML=h;
+}
+
+legend(); build(); timeline(); buildToolbar();
document.getElementById('pyramid').addEventListener('click', onNodeClick);
document.addEventListener('keydown', e=>{ if(e.key==='Escape') closePanel(); });
-requestAnimationFrame(()=>requestAnimationFrame(drawLines));
-window.addEventListener('resize', ()=>{ requestAnimationFrame(drawLines); });
+function redraw(){ drawLines(); drawCross(); }
+requestAnimationFrame(()=>requestAnimationFrame(redraw));
+window.addEventListener('resize', ()=>{ requestAnimationFrame(redraw); });
</script>
</body>
</html>
← 29b288a Make pyramid nodes runnable + openable: /api/source|open|run
·
back to Agent Cabinet
·
Pyramid: clickable suggestion lines — click a dotted arrow t d87e58a →