Files
cad-atlas/internal/web/static/cad-atlas.html
T
mathiasandClaude Opus 4.8 a9c72d6ca8
CD / Detect unsubstituted template (push) Successful in 0s
CD / Lint / Test / Vet (push) Successful in 5s
CD / Build & Import (push) Successful in 14s
CD / Deploy via GitOps (push) Successful in 1s
feat(atlas): Phase B — data-driven /api/atlas.json, CI stage generated from cd.yml
The atlas no longer hand-maintains its content. Authored data lives in one place
(internal/atlas/atlas.json); the Go layer overlays sourced facts and serves the
result at /api/atlas.json; the frontend fetches + renders (inline arrays kept only
as an offline fallback). First generated source: the CI/CD stage's nodes are parsed
from the repo's own .gitea/workflows/cd.yml — so the viz shows the pipeline that
actually runs (guard/check/build/deploy), dropping the aspirational var-go/oath-gate
node that isn't wired yet. That's the point: it can't drift from the real pipeline.

New internal/atlas package (JobsFromWorkflow, Build) built test-first. Adds
gopkg.in/yaml.v3 (justified: parsing the workflow YAML; stdlib has no YAML).

Verified: go build/vet/lint(0)/test green; /api/atlas.json → 9 stages, CI = real
jobs; frontend renders from the fetch (screenshot).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 00:34:06 +02:00

290 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CAD Atlas · From Signal to Pod</title>
<style>
:root{
--bg:#080b12; --panel:#0e1420; --panel2:#121a2b; --line:#1e2b44;
--ink:#cdd9ef; --dim:#7f92b5; --mono:#8fb4ff;
--blue:#4aa8ff; --amber:#f5b942; --coral:#ff7a5c; --green:#4ad07a; --violet:#9b8cff; --gold:#e6c15a;
}
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{
background:
radial-gradient(circle at 1px 1px,#16213a 1px,transparent 0) 0 0/26px 26px,
var(--bg);
color:var(--ink);
font:14px/1.5 ui-sans-serif,system-ui,Segoe UI,Roboto,sans-serif;
-webkit-font-smoothing:antialiased;
}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
header{
position:sticky;top:0;z-index:30;backdrop-filter:blur(8px);
background:linear-gradient(180deg,rgba(8,11,18,.94),rgba(8,11,18,.72));
border-bottom:1px solid var(--line);
display:flex;align-items:baseline;gap:16px;flex-wrap:wrap;padding:14px 22px;
}
header h1{font-size:19px;margin:0}
header h1 b{color:var(--blue)}
header .sub{color:var(--dim);font-size:12px}
header .sub em{color:var(--amber);font-style:normal}
.controls{margin-left:auto;display:flex;gap:8px}
button{
font:inherit;font-size:12px;color:var(--ink);cursor:pointer;
background:var(--panel2);border:1px solid var(--line);border-radius:8px;
padding:7px 13px;display:inline-flex;align-items:center;gap:7px;
}
button:hover{border-color:var(--blue)}
button.on{border-color:var(--amber);color:var(--amber)}
button .dot{width:7px;height:7px;border-radius:50%;background:var(--blue)}
button.on .dot{background:var(--amber)}
.substrate{display:flex;gap:10px;flex-wrap:wrap;align-items:center;
padding:10px 22px;border-bottom:1px solid var(--line);background:var(--panel)}
.substrate .lbl{color:var(--dim);font-size:11px;letter-spacing:1.5px;margin-right:4px}
.host{border:1px solid var(--line);border-radius:8px;padding:6px 11px;
background:var(--panel2);font-size:12px;display:flex;gap:8px;align-items:center}
.host b{color:var(--blue)}
.host .k{color:var(--dim);font-size:11px}
.host.mesh{border-style:dashed;color:var(--dim)}
.scroll{overflow-x:auto;padding:24px 22px 20px}
.track{position:relative;display:flex;align-items:flex-start;min-width:max-content}
svg.spine{position:absolute;left:0;top:0;z-index:0;pointer-events:none;overflow:visible}
.pulse{position:absolute;top:0;left:0;width:13px;height:13px;border-radius:50%;
background:var(--amber);box-shadow:0 0 15px 4px rgba(245,185,66,.75);
transform:translate(-6.5px,-6.5px);z-index:6}
.stage{width:300px;flex:0 0 300px;padding:0 14px;position:relative;z-index:2}
.stage .no{color:var(--dim);font-size:11px;letter-spacing:2px}
.stage h2{font-size:17px;margin:6px 0 2px}
.stage .path{color:var(--mono);font-size:11.5px;margin-bottom:6px;min-height:16px}
.node{border:1px solid var(--line);border-radius:11px;background:var(--panel);
padding:12px 13px;margin-top:12px;position:relative;
transition:border-color .25s,box-shadow .25s,transform .25s}
.node .t{font-weight:600;margin-bottom:3px;display:flex;align-items:center;gap:7px}
.node .d{color:var(--dim);font-size:12px}
.node .tag{display:inline-block;font-size:10.5px;padding:2px 7px;border-radius:20px;
border:1px solid var(--line);color:var(--dim);margin:6px 6px 0 0}
.pill{width:8px;height:8px;border-radius:50%;flex:0 0 8px}
.stage.telos h2{color:var(--violet)}
.stage.gate h2{color:var(--amber)}
.stage.exec h2{color:var(--coral)}
.stage.cd h2{color:var(--green)}
.node.win{border-color:var(--coral);box-shadow:0 0 0 1px rgba(255,122,92,.25)}
.node.gateway{border-color:var(--amber);background:linear-gradient(180deg,rgba(245,185,66,.06),transparent)}
.node.council{border-color:var(--violet)}
.node.bridge{border-color:var(--blue);border-style:dashed}
.node.oath{border-color:var(--gold);border-style:dashed;background:linear-gradient(180deg,rgba(230,193,90,.05),transparent)}
.stage.active h2{text-shadow:0 0 18px currentColor}
.stage.active .node{border-color:var(--amber);
box-shadow:0 0 0 1px rgba(245,185,66,.35),0 10px 30px -12px rgba(245,185,66,.4);transform:translateY(-2px)}
.risk{display:flex;gap:6px;margin-top:8px;flex-wrap:wrap}
.risk span{font-size:10.5px;padding:2px 7px;border-radius:6px;border:1px solid var(--line)}
.risk .lo{color:var(--green);border-color:rgba(74,208,122,.4)}
.risk .md{color:var(--amber);border-color:rgba(245,185,66,.4)}
.risk .hi{color:var(--coral);border-color:rgba(255,122,92,.4)}
.gatebtns{display:flex;gap:8px;margin-top:10px}
.gatebtns .g{flex:1;text-align:center;padding:8px;border-radius:8px;font-size:12px;border:1px solid}
.gatebtns .ok{border-color:rgba(74,208,122,.5);color:var(--green)}
.gatebtns .no{border-color:rgba(255,122,92,.5);color:var(--coral)}
footer{padding:14px 22px 30px;color:var(--dim);font-size:11px;border-top:1px solid var(--line)}
footer code{color:var(--mono)}
/* ---- responsive: stack vertically, drop the SVG spine ---- */
@media(max-width:820px){
.scroll{overflow-x:visible;padding:16px}
.track{flex-direction:column;min-width:0}
svg.spine,.pulse{display:none}
.stage{width:100%;flex:auto;padding:0;border-left:2px solid var(--line);margin-left:6px;
padding-left:16px;padding-bottom:8px}
.stage.telos{border-color:var(--violet)} .stage.gate{border-color:var(--amber)}
.stage.exec{border-color:var(--coral)} .stage.cd{border-color:var(--green)}
}
</style>
</head>
<body>
<header>
<h1><b>CAD</b> Atlas · From Signal to Pod</h1>
<span class="sub mono">one human gate · everything up- and downstream is agents · <em>v0.4 · data-driven (CI stage generated from cd.yml)</em></span>
<div class="controls">
<button id="replay"><span class="dot"></span> Replay</button>
<button id="slowmo">Slow-mo · <span id="slowState">off</span></button>
</div>
</header>
<div class="substrate" id="substrate"><span class="lbl mono">SUBSTRATE</span></div>
<div class="scroll">
<div class="track" id="track">
<svg class="spine" id="spine">
<defs>
<marker id="arw" markerWidth="9" markerHeight="9" refX="6" refY="3" orient="auto">
<path d="M0,0 L6,3 L0,6 Z" fill="#33507f"></path>
</marker>
</defs>
<path id="spinePath" fill="none" stroke="#33507f" stroke-width="2"
marker-mid="url(#arw)" marker-end="url(#arw)"></path>
<path id="loopPath" fill="none" stroke="#9b8cff" stroke-width="1.6"
stroke-dasharray="5 5" opacity=".7"></path>
<text id="loopLbl" fill="#9b8cff" font-size="11"
font-family="ui-monospace,monospace" opacity=".85"></text>
</svg>
<div class="pulse" id="pulse"></div>
</div>
</div>
<footer class="mono">
CAD → CI → CD · intent→specify→dispatch · build→test→validate · deploy→ship.
Dashed violet = feedback bus (stage 08 → TELOS: deploy outcome scored vs originating goal).
Data served from <code>/api/atlas.json</code> (authored <code>atlas.json</code> + CI stage generated from the live <code>cd.yml</code>).
Phase C plugs in live reads of <code>assessor-loop</code> ledger · <code>session_log</code> · Gitea run API · Flux events.
</footer>
<script>
// Inline data is an offline FALLBACK. Authoritative data is fetched from
// /api/atlas.json (authored atlas.json + CI stage generated from the real cd.yml).
let SUBSTRATE=[
{n:"koala", k:"RTX 5070 · k3s control-plane · Gitea · LiteLLM :30401 · llama-swap :31234 · searxng"},
{n:"iguana", k:"M2 Ultra · Ollama / mlx"},
{n:"flamingo",k:"daily driver · ~/dev"},
{n:"piguard",k:"NGINX reverse-proxy · ntfy"},
];
let NS="Tailscale mesh · ns: ai-stack · supervisor(→brain) · gitea-mcp · infra-mcp · council";
let STAGES=[
{no:"STAGE 00",cls:"",title:"Signals",path:"→ mathias/signals",nodes:[
{t:"Applied AI Radar",d:"Daily Tier-1 + weekly Tier-2 deep pass. Verified-primary bar (paper/benchmark/code/named-lab).",tags:["cron · daily/weekly","→ signals #126+"]},
{t:"Manual capture",d:"claude.ai strategic drop · brain capture tool.",tags:["ad-hoc"]},
{t:"Aspirational surfaces",pill:"var(--dim)",d:"Telegram / voice / URL → inbox. NOT built.",tags:["gap"]},
]},
{no:"STAGE 01",cls:"telos",title:"TELOS",path:"wiki/telos/",nodes:[
{t:"Intention substrate",pill:"var(--violet)",d:"Mission · goals · problems · strategies · status. Every downstream item traces to a goal.",tags:["brain_query wing=telos"]},
]},
{no:"STAGE 02",cls:"",title:"Strategic session",path:"claude.ai frontier + brain MCP",nodes:[
{t:"Design · ADRs · specs",d:"Human + frontier model. ISC acceptance criteria written here.",tags:["Define / converge"]},
{t:"🏛️ LLM Council",cls:"council",pill:"var(--violet)",d:"fan-out → anonymous cross-review → chairman synth. glm-4.7-flash · qwen36-35b · gemma4-31b (chair).",tags:["hard strategic Q","chat.d-ma.be"]},
{t:"Autoresearch Council",cls:"council",pill:"var(--violet)",d:"Sibling pipe — ratifies research before the gate.",tags:["proposed: → standalone svc"]},
]},
{no:"STAGE 03",cls:"",title:"Spec → Gitea issue",path:"agent-ready contract",nodes:[
{t:"Contract enforced",d:"Binary ISC · declared risk tier · reg-risk assessment · no open human deps.",tags:["LOW / MED / HIGH"]},
{t:"Admission controller",d:"Ed25519-sign issue body at creation (#36). Verify sig + PR alignment at infra boundary.",tags:["chain of custody"]},
{t:"⚖️ var-go Oath",cls:"oath",pill:"var(--gold)",d:"Acceptance contract embedded in the issue as a ```var fenced block. Exactly one — zero/multiple fail closed. Prose → typed steps; failures anchored to byte spans.",tags:["swedsl · var-go","defined here → enforced @06"]},
]},
{no:"STAGE 04",cls:"gate",title:"Human dispatch gate",path:"the only checkpoint",nodes:[
{t:"Human triggers execution",cls:"gateway",pill:"var(--amber)",d:"Ratify proposed-plan + risk tier, then dispatch.",gate:true},
{t:"Session-Dispatch bridge",cls:"bridge",pill:"var(--blue)",d:"claude.ai MCP → gitea:workflow_run_trigger → cad-dispatch.yml → agentsquad. The final design→execution bridge.",tags:["workflow_dispatch"]},
]},
{no:"STAGE 05",cls:"exec",title:"Execute · agentsquad",path:"koala · cmd/agentsquad-serve",nodes:[
{t:"Task API",pill:"var(--coral)",d:"POST /tasks → job id · GET /tasks/{id}. taskqueue + serve (v0.12+).",tags:["single agentsquad.yaml"]},
{t:"Executor + reviewer loop",cls:"win",pill:"var(--coral)",d:"ADK Go + LiteLLM. Frontier models (local qwen spirals). Reviewer on distinct tier — echo-chamber prevention.",risk:true},
{t:"dma-cli · routing + scope",cls:"bridge",pill:"var(--blue)",d:"Harness-config arm: routes agents to the right LLM backend. Three-layer scope policy + confirmation gate = CAD guardrail.",tags:["backend routing","scope guardrail"]},
{t:"assessor-loop ledger",d:"Attestation ledger (audit trail) + brain session_log on completion.",tags:["audit package"]},
]},
{no:"STAGE 06",cls:"",title:"PR → CI",path:"Gitea Actions",nodes:[
{t:"PR + label",d:"Gitea PR · agent-done / agent-blocked label.",tags:[]},
{t:"Mechanical gate",d:"go test · vet · lint · govulncheck. ISC verified mechanically.",tags:["green = proceed"]},
{t:"⚖️ var-go/oath gate",cls:"oath",pill:"var(--gold)",d:"cmd/vargo-gate runs in CI → posts commit status context=var-go/oath. Authoritative FLOOR: failed Oath blocks regardless of reviewer approval (#55 anti-rubber-stamp).",tags:["branch-protection req","enforces @03 Oath"]},
]},
{no:"STAGE 07",cls:"cd",title:"CD → pod",path:"Flux GitOps → k3s",nodes:[
{t:"Deploy on green",pill:"var(--green)",d:"Flux reconciles image → k3s pod on koala. Push ≠ deploy: bump tag in mathias/infra.",tags:["ntfy on deploy"]},
]},
{no:"STAGE 08",cls:"telos",title:"Loop back",path:"→ TELOS (feedback bus)",nodes:[
{t:"Close the loop",pill:"var(--violet)",d:"session_log + attestation → brain. Score deploy outcome vs originating goal. (arc partly manual — improvement target.)",tags:["continuous"]},
]},
];
const track=document.getElementById('track');
let stageEls=[];
function renderAtlas(){
const sub=document.getElementById('substrate');
sub.querySelectorAll('.host').forEach(el=>el.remove());
SUBSTRATE.forEach(h=>{const el=document.createElement('div');el.className='host';
el.innerHTML=`<b>${h.n}</b><span class="k mono">${h.k}</span>`;sub.appendChild(el);});
const mesh=document.createElement('div');mesh.className='host mesh mono';mesh.textContent=NS;sub.appendChild(mesh);
stageEls=[];
track.querySelectorAll('.stage').forEach(el=>el.remove());
STAGES.forEach(s=>{
const st=document.createElement('div');st.className='stage '+s.cls;
let h=`<div class="no mono">${s.no}</div><h2>${s.title}</h2><div class="path mono">${s.path||''}</div>`;
s.nodes.forEach(n=>{
const pill=n.pill?`<span class="pill" style="background:${n.pill}"></span>`:'';
let inner=`<div class="t">${pill}${n.t}</div><div class="d">${n.d}</div>`;
if(n.tags&&n.tags.length)inner+=n.tags.map(t=>`<span class="tag mono">${t}</span>`).join('');
if(n.risk)inner+=`<div class="risk mono"><span class="lo">LOW · auto</span><span class="md">MED · ntfy gate</span><span class="hi">HIGH · blocked</span></div>`;
if(n.gate)inner+=`<div class="gatebtns mono"><div class="g ok">✓ approve</div><div class="g no">✕ reject</div></div>`;
h+=`<div class="node ${n.cls||''}">${inner}</div>`;
});
st.innerHTML=h;track.appendChild(st);stageEls.push(st);
});
}
/* ---- geometry ---- */
const spine=document.getElementById('spine'), spinePath=document.getElementById('spinePath'),
loopPath=document.getElementById('loopPath'), loopLbl=document.getElementById('loopLbl'),
pulse=document.getElementById('pulse');
const RAILY=70;
let cs=[],loopY=0,spineLen=0,loopLen=0,slow=false,raf=null,t0=null,mobile=false;
function build(){
mobile=window.matchMedia('(max-width:820px)').matches;
if(mobile)return;
cs=stageEls.map(s=>s.offsetLeft+s.offsetWidth/2);
const maxBottom=Math.max(...stageEls.map(s=>s.offsetTop+s.offsetHeight));
loopY=maxBottom+40;
spine.setAttribute('width',track.scrollWidth);
spine.setAttribute('height',loopY+70);
track.style.minHeight=(loopY+70)+'px';
spinePath.setAttribute('d','M '+cs[0]+' '+RAILY+cs.slice(1).map(x=>' L '+x+' '+RAILY).join(''));
const lastX=cs[cs.length-1], telosX=cs[1];
loopPath.setAttribute('d',`M ${lastX} ${RAILY} L ${lastX} ${loopY} L ${telosX} ${loopY} L ${telosX} ${RAILY}`);
loopLbl.setAttribute('x',(telosX+lastX)/2-90);loopLbl.setAttribute('y',loopY-8);
loopLbl.textContent='feedback bus · outcome → goal';
spineLen=spinePath.getTotalLength();loopLen=loopPath.getTotalLength();
}
function run(ts){
if(mobile)return;
if(!t0)t0=ts;
const dur=slow?16000:7000;
const p=Math.min((ts-t0)/dur,1);
const total=spineLen+loopLen, dist=total*p;
let pt,onLoop=dist>spineLen;
pt=onLoop?loopPath.getPointAtLength(dist-spineLen):spinePath.getPointAtLength(dist);
pulse.style.left=pt.x+'px';pulse.style.top=pt.y+'px';
pulse.style.background=onLoop?'var(--violet)':'var(--amber)';
pulse.style.boxShadow=onLoop?'0 0 15px 4px rgba(155,140,255,.75)':'0 0 15px 4px rgba(245,185,66,.75)';
let act=onLoop?1:0; // during loop, highlight TELOS
if(!onLoop)cs.forEach((c,i)=>{if(pt.x>=c-150)act=i;});
stageEls.forEach((s,i)=>s.classList.toggle('active',i===act));
if(p<1)raf=requestAnimationFrame(run);
}
function replay(){cancelAnimationFrame(raf);t0=null;build();if(!mobile)raf=requestAnimationFrame(run);}
document.getElementById('replay').onclick=replay;
document.getElementById('slowmo').onclick=e=>{slow=!slow;e.currentTarget.classList.toggle('on',slow);
document.getElementById('slowState').textContent=slow?'on':'off';replay();};
window.addEventListener('resize',()=>{clearTimeout(window._r);window._r=setTimeout(replay,150);});
async function init(){
try{
const r=await fetch('/api/atlas.json');
if(!r.ok) throw new Error('atlas.json '+r.status);
const data=await r.json();
if(Array.isArray(data.substrate)) SUBSTRATE=data.substrate;
if(typeof data.ns==='string') NS=data.ns;
if(Array.isArray(data.stages)) STAGES=data.stages;
}catch(e){ console.warn('atlas: using inline fallback —',e); }
renderAtlas();
replay();
}
window.addEventListener('load',init);
</script>
</body>
</html>