stratumiops/assets/orchestrator/w-flow-stratum-build-pipeline.svg

451 lines
36 KiB
XML
Raw Permalink Normal View History

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1600 820" width="1600" height="820">
<defs>
<filter id="glow-c" x="-40%" y="-40%" width="180%" height="180%">
<feGaussianBlur stdDeviation="4" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glow-g" x="-40%" y="-40%" width="180%" height="180%">
<feGaussianBlur stdDeviation="3" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glow-i" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur stdDeviation="3" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<marker id="ac" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<path d="M0,0 L0,6 L8,3 z" fill="#22D3EE"/>
</marker>
<marker id="ai" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<path d="M0,0 L0,6 L8,3 z" fill="#6366F1"/>
</marker>
<marker id="ag" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<path d="M0,0 L0,6 L8,3 z" fill="#F59E0B"/>
</marker>
<marker id="agr" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<path d="M0,0 L0,6 L8,3 z" fill="#10B981"/>
</marker>
<marker id="ap" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<path d="M0,0 L0,6 L8,3 z" fill="#A78BFA"/>
</marker>
<marker id="ar" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<path d="M0,0 L0,6 L8,3 z" fill="#F43F5E"/>
</marker>
<marker id="aib" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<path d="M0,0 L0,6 L8,3 z" fill="#818CF8"/>
</marker>
<marker id="ail" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<path d="M0,0 L0,6 L8,3 z" fill="#A5B4FC"/>
</marker>
<marker id="ao" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<path d="M0,0 L0,6 L8,3 z" fill="#F97316"/>
</marker>
<!-- ─── stratumiops-h logo defs ─── -->
<linearGradient id="hpLayerGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1"/><stop offset="100%" style="stop-color:#4F46E5"/>
</linearGradient>
<linearGradient id="hpProcessorGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#22D3EE"/><stop offset="100%" style="stop-color:#06B6D4"/>
</linearGradient>
<linearGradient id="hpFlowGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#6366F1"/><stop offset="50%" style="stop-color:#22D3EE"/><stop offset="100%" style="stop-color:#6366F1"/>
<animate attributeName="x1" values="0%;100%;0%" dur="2.5s" repeatCount="indefinite"/>
<animate attributeName="x2" values="100%;200%;100%" dur="2.5s" repeatCount="indefinite"/>
</linearGradient>
<linearGradient id="hpUnderlineGrad" x1="280" y1="195" x2="750" y2="195" gradientUnits="userSpaceOnUse">
<stop offset="0%" style="stop-color:#6366F1"/><stop offset="50%" style="stop-color:#22D3EE"/><stop offset="100%" style="stop-color:#6366F1"/>
</linearGradient>
<linearGradient id="hpShimmer" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#6366F1"/><stop offset="40%" style="stop-color:#6366F1"/>
<stop offset="50%" style="stop-color:#22D3EE"/><stop offset="60%" style="stop-color:#6366F1"/><stop offset="100%" style="stop-color:#6366F1"/>
<animate attributeName="x1" values="-100%;100%" dur="3s" repeatCount="indefinite" begin="2s"/>
<animate attributeName="x2" values="0%;200%" dur="3s" repeatCount="indefinite" begin="2s"/>
</linearGradient>
<filter id="hpProcessorGlow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="5" result="coloredBlur"><animate attributeName="stdDeviation" values="4;8;4" dur="1.5s" repeatCount="indefinite"/></feGaussianBlur>
<feMerge><feMergeNode in="coloredBlur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="hpNodeGlow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
<feMerge><feMergeNode in="coloredBlur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<clipPath id="hpTextReveal">
<rect x="280" y="80" width="0" height="150">
<animate attributeName="width" values="0;800" dur="1s" fill="freeze" begin="1s" calcMode="spline" keySplines="0.25 0.1 0.25 1"/>
</rect>
</clipPath>
<path id="hpPathIn1" d="M25 40 Q25 65 55 80 Q85 95 95 100" fill="none"/>
<path id="hpPathIn2" d="M215 40 Q215 65 185 80 Q155 95 145 100" fill="none"/>
<path id="hpPathOut1" d="M95 100 Q85 105 55 120 Q25 135 25 160" fill="none"/>
<path id="hpPathOut2" d="M145 100 Q155 105 185 120 Q215 135 215 160" fill="none"/>
</defs>
<style>
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.65} }
@keyframes nats-glow { 0%,100%{stroke-opacity:1;filter:url(#glow-c)} 50%{stroke-opacity:0.5;filter:none} }
@keyframes auth-glow { 0%,100%{stroke-opacity:1;filter:url(#glow-g)} 50%{stroke-opacity:0.4;filter:none} }
@keyframes flow-dash { to{stroke-dashoffset:-20} }
@keyframes flow-dash-slow { to{stroke-dashoffset:-20} }
.fd { animation: flow-dash 1.2s linear infinite; }
.fds { animation: flow-dash-slow 2s linear infinite; }
.nd { animation: nats-glow 1.8s ease-in-out infinite; }
.ah { animation: auth-glow 2s ease-in-out infinite; }
.p1 { animation: pulse 2.4s ease-in-out 0s infinite; }
.p2 { animation: pulse 2.4s ease-in-out 0.2s infinite; }
.p3 { animation: pulse 2.4s ease-in-out 0.4s infinite; }
.p4 { animation: pulse 2.4s ease-in-out 0.6s infinite; }
.p5 { animation: pulse 2.4s ease-in-out 0.8s infinite; }
.p6 { animation: pulse 2.4s ease-in-out 1.0s infinite; }
.p7 { animation: pulse 2.4s ease-in-out 1.2s infinite; }
.p8 { animation: pulse 2.4s ease-in-out 1.4s infinite; }
.p9 { animation: pulse 2.4s ease-in-out 1.6s infinite; }
.p10 { animation: pulse 2.4s ease-in-out 1.8s infinite; }
.p11 { animation: pulse 2.4s ease-in-out 0.3s infinite; }
.p12 { animation: pulse 2.4s ease-in-out 0.5s infinite; }
</style>
<!-- Background -->
<rect width="1600" height="820" fill="#F8FAFC"/>
<!-- TITLE BAR -->
<rect x="0" y="0" width="1600" height="2" fill="#22D3EE"/>
<rect x="0" y="0" width="1600" height="99" fill="#F1F5F9"/>
<text x="800" y="40" font-family="'IBM Plex Mono',monospace" font-size="19" fill="#0891B2" text-anchor="middle" font-weight="500" letter-spacing="2">STRATUM ORCHESTRATOR · BUILD PIPELINE FLOW</text>
<text x="800" y="66" font-family="'IBM Plex Mono',monospace" font-size="12" fill="#475569" text-anchor="middle">event: dev.crate.provisioning-cli.modified · trigger → ActionGraph → stages → state → emit</text>
<rect x="0" y="98" width="1600" height="2" fill="#E2E8F0"/>
<!-- LANE LABEL COLUMN SEPARATOR -->
<line x1="182" y1="100" x2="182" y2="742" stroke="#CBD5E1" stroke-width="2"/>
<!-- Lane 0: Developer / Forgejo -->
<rect x="0" y="100" width="6" height="80" fill="#F97316"/>
<rect x="6" y="100" width="176" height="80" fill="#F97316" fill-opacity="0.12"/>
<text x="22" y="131" font-family="'Inter',sans-serif" font-size="10" fill="#F97316" font-weight="600">Developer · Forgejo</text>
<text x="22" y="147" font-family="'Inter',sans-serif" font-size="9" fill="#64748B">git push / webhook</text>
<!-- Lane 1: NATS JetStream -->
<rect x="0" y="180" width="6" height="80" fill="#22D3EE"/>
<rect x="6" y="180" width="176" height="80" fill="#22D3EE" fill-opacity="0.10"/>
<text x="22" y="211" font-family="'Inter',sans-serif" font-size="10" fill="#22D3EE" font-weight="600">NATS JetStream</text>
<text x="22" y="227" font-family="'Inter',sans-serif" font-size="9" fill="#64748B">Event Bus</text>
<!-- Lane 2: Orchestrator Planning -->
<rect x="0" y="260" width="6" height="80" fill="#6366F1"/>
<rect x="6" y="260" width="176" height="80" fill="#6366F1" fill-opacity="0.10"/>
<text x="22" y="291" font-family="'Inter',sans-serif" font-size="10" fill="#6366F1" font-weight="600">Orchestrator</text>
<text x="22" y="307" font-family="'Inter',sans-serif" font-size="9" fill="#64748B">Planning</text>
<!-- Lane 3: Auth -->
<rect x="0" y="340" width="6" height="80" fill="#F59E0B"/>
<rect x="6" y="340" width="176" height="80" fill="#F59E0B" fill-opacity="0.10"/>
<text x="22" y="371" font-family="'Inter',sans-serif" font-size="10" fill="#F59E0B" font-weight="600">Auth · NKeys</text>
<text x="22" y="387" font-family="'Inter',sans-serif" font-size="9" fill="#64748B">+ Cedar</text>
<!-- Lane 4: Stage 0 Parallel -->
<rect x="0" y="420" width="6" height="80" fill="#10B981"/>
<rect x="6" y="420" width="176" height="80" fill="#10B981" fill-opacity="0.10"/>
<text x="22" y="451" font-family="'Inter',sans-serif" font-size="10" fill="#10B981" font-weight="600">Stage 0</text>
<text x="22" y="467" font-family="'Inter',sans-serif" font-size="9" fill="#64748B">‖ Parallel</text>
<!-- Lane 5: Stage 1 -->
<rect x="0" y="500" width="6" height="80" fill="#818CF8"/>
<rect x="6" y="500" width="176" height="80" fill="#818CF8" fill-opacity="0.08"/>
<text x="22" y="531" font-family="'Inter',sans-serif" font-size="10" fill="#818CF8" font-weight="600">Stage 1</text>
<text x="22" y="547" font-family="'Inter',sans-serif" font-size="9" fill="#64748B">→ Sequential</text>
<!-- Lane 6: Stage 2 -->
<rect x="0" y="580" width="6" height="80" fill="#A5B4FC"/>
<rect x="6" y="580" width="176" height="80" fill="#A5B4FC" fill-opacity="0.07"/>
<text x="22" y="611" font-family="'Inter',sans-serif" font-size="10" fill="#A5B4FC" font-weight="600">Stage 2</text>
<text x="22" y="627" font-family="'Inter',sans-serif" font-size="9" fill="#64748B">→ Sequential</text>
<!-- Lane 7: SurrealDB -->
<rect x="0" y="660" width="6" height="80" fill="#A78BFA"/>
<rect x="6" y="660" width="176" height="80" fill="#A78BFA" fill-opacity="0.10"/>
<text x="22" y="691" font-family="'Inter',sans-serif" font-size="10" fill="#A78BFA" font-weight="600">SurrealDB</text>
<text x="22" y="707" font-family="'Inter',sans-serif" font-size="9" fill="#64748B">State</text>
<!-- LANE DIVIDERS (content area) -->
<line x1="182" y1="180" x2="1590" y2="180" stroke="#CBD5E1" stroke-width="1"/>
<line x1="182" y1="260" x2="1590" y2="260" stroke="#CBD5E1" stroke-width="1"/>
<line x1="182" y1="340" x2="1590" y2="340" stroke="#CBD5E1" stroke-width="1"/>
<line x1="182" y1="420" x2="1590" y2="420" stroke="#CBD5E1" stroke-width="1"/>
<line x1="182" y1="500" x2="1590" y2="500" stroke="#CBD5E1" stroke-width="1"/>
<line x1="182" y1="580" x2="1590" y2="580" stroke="#CBD5E1" stroke-width="1"/>
<line x1="182" y1="660" x2="1590" y2="660" stroke="#CBD5E1" stroke-width="1"/>
<line x1="182" y1="740" x2="1590" y2="740" stroke="#CBD5E1" stroke-width="1"/>
<!-- TIMELINE BASELINE (dashed, very subtle) -->
<path id="tl" d="M200,140 L1560,140" fill="none" stroke="#F97316" stroke-width="1" stroke-dasharray="5,4" opacity="0.2" class="fd"/>
<!-- ANIMATED PARTICLES along timeline -->
<circle r="5" fill="#22D3EE" opacity="0.85" filter="url(#glow-c)">
<animateMotion dur="6s" repeatCount="indefinite" begin="0s">
<mpath xlink:href="#tl"/>
</animateMotion>
</circle>
<circle r="4" fill="#6366F1" opacity="0.7">
<animateMotion dur="6s" repeatCount="indefinite" begin="2s">
<mpath xlink:href="#tl"/>
</animateMotion>
</circle>
<circle r="3.5" fill="#F59E0B" opacity="0.65">
<animateMotion dur="6s" repeatCount="indefinite" begin="4s">
<mpath xlink:href="#tl"/>
</animateMotion>
</circle>
<!-- ========== STEP 1: git push (Developer, x=220, yc=140) ========== -->
<g class="p1">
<circle cx="220" cy="112" r="9" fill="#F97316"/>
<text x="220" y="116" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">1</text>
<rect x="185" y="120" width="70" height="40" rx="5" fill="#F97316" fill-opacity="0.18" stroke="#F97316" stroke-width="1.5"/>
<text x="220" y="136" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#F97316" text-anchor="middle" font-weight="500">git push</text>
<text x="220" y="150" font-family="'Inter',sans-serif" font-size="8" fill="#64748B" text-anchor="middle">code change</text>
<text x="220" y="170" font-family="'IBM Plex Mono',monospace" font-size="7.5" fill="#334155" text-anchor="middle">commit: abc123f</text>
</g>
<!-- ========== STEP 2: webhook→NATS diamond (spans Developer/NATS border at y=180, x=330) ========== -->
<g class="nd p2">
<circle cx="330" cy="112" r="9" fill="#F97316"/>
<text x="330" y="116" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">2</text>
<polygon points="330,128 400,178 330,228 260,178" fill="#22D3EE" fill-opacity="0.12" stroke="#22D3EE" stroke-width="1.5"/>
<text x="330" y="163" font-family="'IBM Plex Mono',monospace" font-size="8" fill="#22D3EE" text-anchor="middle">dev.crate.</text>
<text x="330" y="178" font-family="'IBM Plex Mono',monospace" font-size="8" fill="#22D3EE" text-anchor="middle">provisioning-cli</text>
<text x="330" y="193" font-family="'IBM Plex Mono',monospace" font-size="8" fill="#22D3EE" text-anchor="middle">.modified</text>
<rect x="283" y="220" width="96" height="24" rx="3" fill="#F1F5F9" stroke="#CBD5E1" stroke-width="1"/>
<text x="330" y="231" font-family="'IBM Plex Mono',monospace" font-size="7" fill="#334155" text-anchor="middle">&#123;"crate":"prvng-cli",</text>
<text x="330" y="241" font-family="'IBM Plex Mono',monospace" font-size="7" fill="#334155" text-anchor="middle">"sha":"abc123f"&#125;</text>
</g>
<!-- ========== STEP 3: pull_batch(10) (Orchestrator, x=450, yc=300) ========== -->
<g class="p3">
<circle cx="450" cy="268" r="9" fill="#6366F1"/>
<text x="450" y="272" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">3</text>
<rect x="415" y="277" width="70" height="44" rx="5" fill="#6366F1" fill-opacity="0.18" stroke="#6366F1" stroke-width="1.5"/>
<text x="450" y="294" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#818CF8" text-anchor="middle" font-weight="500">pull_batch</text>
<text x="450" y="307" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#818CF8" text-anchor="middle">(10)</text>
<text x="450" y="332" font-family="'Inter',sans-serif" font-size="8" fill="#334155" text-anchor="middle">JetStream consumer</text>
</g>
<!-- ========== STEP 4: NKey verify (Auth hexagon, x=565, yc=382) ========== -->
<g class="ah p4">
<circle cx="565" cy="348" r="9" fill="#F59E0B"/>
<text x="565" y="352" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">4</text>
<polygon points="565,352 601,370 601,401 565,419 530,401 530,370" fill="#F59E0B" fill-opacity="0.12" stroke="#F59E0B" stroke-width="1.5"/>
<text x="565" y="381" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#F59E0B" text-anchor="middle">NKey</text>
<text x="565" y="394" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#F59E0B" text-anchor="middle">verify</text>
<text x="565" y="428" font-family="'Inter',sans-serif" font-size="8" fill="#334155" text-anchor="middle">publisher auth</text>
</g>
<!-- ========== STEP 5: ActionGraph query+topo-sort (Orchestrator, x=688, yc=299, wider) ========== -->
<g class="p5">
<circle cx="688" cy="268" r="9" fill="#6366F1"/>
<text x="688" y="272" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">5</text>
<rect x="644" y="277" width="88" height="46" rx="5" fill="#6366F1" fill-opacity="0.22" stroke="#6366F1" stroke-width="2" filter="url(#glow-i)"/>
<text x="688" y="293" font-family="'IBM Plex Mono',monospace" font-size="8.5" fill="#3730A3" text-anchor="middle" font-weight="600">ActionGraph</text>
<text x="688" y="306" font-family="'IBM Plex Mono',monospace" font-size="7.5" fill="#4338CA" text-anchor="middle">query + topo-sort</text>
<!-- topo order annotation -->
<rect x="644" y="330" width="88" height="36" rx="3" fill="#F1F5F9" stroke="#CBD5E1" stroke-width="1"/>
<text x="688" y="341" font-family="'IBM Plex Mono',monospace" font-size="6.5" fill="#334155" text-anchor="middle">[lint-crate, fmt-crate]</text>
<text x="688" y="351" font-family="'IBM Plex Mono',monospace" font-size="6.5" fill="#334155" text-anchor="middle">→ build-crate</text>
<text x="688" y="361" font-family="'IBM Plex Mono',monospace" font-size="6.5" fill="#334155" text-anchor="middle">→ install</text>
<!-- Stages bracket -->
<line x1="740" y1="277" x2="740" y2="502" stroke="#334155" stroke-width="1" stroke-dasharray="3,3"/>
<text x="754" y="396" font-family="'Inter',sans-serif" font-size="8" fill="#64748B" text-anchor="middle" transform="rotate(90,754,396)">Stages</text>
</g>
<!-- ========== STEP 6: Cedar authorize (Auth hexagon, x=800, yc=382) ========== -->
<g class="ah p6">
<circle cx="800" cy="348" r="9" fill="#F59E0B"/>
<text x="800" y="352" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">6</text>
<polygon points="800,352 836,370 836,401 800,419 765,401 765,370" fill="#F59E0B" fill-opacity="0.12" stroke="#F59E0B" stroke-width="1.5"/>
<text x="800" y="381" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#F59E0B" text-anchor="middle">Cedar</text>
<text x="800" y="394" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#F59E0B" text-anchor="middle">authorize</text>
<text x="800" y="425" font-family="'IBM Plex Mono',monospace" font-size="7" fill="#334155" text-anchor="middle">permit: trigger</text>
<text x="800" y="435" font-family="'IBM Plex Mono',monospace" font-size="7" fill="#334155" text-anchor="middle">node:build-crate</text>
</g>
<!-- ========== STEP 7: Stage 0 PARALLEL wide box (Stage 0, x=876-1036, yc=460) ========== -->
<!-- parallel bracket above -->
<path d="M878,428 L878,423 L1038,423 L1038,428" fill="none" stroke="#10B981" stroke-width="1.2" stroke-dasharray="3,2"/>
<text x="958" y="420" font-family="'Inter',sans-serif" font-size="9" fill="#10B981" text-anchor="middle" font-weight="600">‖ parallel · JoinSet</text>
<g class="p7">
<circle cx="878" cy="430" r="9" fill="#10B981"/>
<text x="878" y="434" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">7</text>
<!-- wide rect -->
<rect x="878" y="437" width="160" height="44" rx="5" fill="#10B981" fill-opacity="0.15" stroke="#10B981" stroke-width="1.5"/>
<!-- center divider -->
<line x1="958" y1="437" x2="958" y2="481" stroke="#10B981" stroke-width="1" stroke-dasharray="3,2" opacity="0.6"/>
<!-- LEFT: lint.nu -->
<text x="918" y="455" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#10B981" text-anchor="middle" font-weight="500">lint.nu</text>
<text x="918" y="469" font-family="'Inter',sans-serif" font-size="8" fill="#64748B" text-anchor="middle">(clippy)</text>
<!-- RIGHT: fmt.nu -->
<text x="998" y="455" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#10B981" text-anchor="middle" font-weight="500">fmt.nu</text>
<text x="998" y="469" font-family="'Inter',sans-serif" font-size="8" fill="#64748B" text-anchor="middle">(cargo fmt)</text>
</g>
<!-- join arc below -->
<path d="M878,481 L878,494 L1038,494 L1038,481" fill="none" stroke="#10B981" stroke-width="1" stroke-dasharray="3,2" opacity="0.5"/>
<text x="958" y="506" font-family="'IBM Plex Mono',monospace" font-size="7.5" fill="#334155" text-anchor="middle">join_all → PipelineContext</text>
<!-- ========== STEP 8: deposit capabilities (Orchestrator, x=1075, yc=300) ========== -->
<g class="p8">
<circle cx="1075" cy="268" r="9" fill="#6366F1"/>
<text x="1075" y="272" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">8</text>
<rect x="1038" y="277" width="74" height="46" rx="5" fill="#6366F1" fill-opacity="0.15" stroke="#6366F1" stroke-width="1.5"/>
<text x="1075" y="293" font-family="'IBM Plex Mono',monospace" font-size="8.5" fill="#818CF8" text-anchor="middle" font-weight="500">deposit:</text>
<text x="1075" y="307" font-family="'IBM Plex Mono',monospace" font-size="7.5" fill="#64748B" text-anchor="middle">linted-code</text>
<text x="1075" y="318" font-family="'IBM Plex Mono',monospace" font-size="7.5" fill="#64748B" text-anchor="middle">formatted-code</text>
</g>
<!-- ========== STEP 9: Stage 1: build.nu (Stage 1, x=1190, yc=540) ========== -->
<g class="p9">
<circle cx="1190" cy="508" r="9" fill="#818CF8"/>
<text x="1190" y="512" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">9</text>
<rect x="1153" y="517" width="74" height="44" rx="5" fill="#818CF8" fill-opacity="0.15" stroke="#818CF8" stroke-width="1.5"/>
<text x="1190" y="533" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#818CF8" text-anchor="middle" font-weight="500">build.nu</text>
<text x="1190" y="546" font-family="'Inter',sans-serif" font-size="8" fill="#64748B" text-anchor="middle">(cargo build)</text>
<!-- Vault annotation -->
<rect x="1153" y="567" width="74" height="22" rx="3" fill="#F1F5F9" stroke="#F59E0B" stroke-width="1" stroke-dasharray="3,2"/>
<text x="1190" y="578" font-family="'IBM Plex Mono',monospace" font-size="7.5" fill="#F59E0B" text-anchor="middle">&#x1F510; Vault cred</text>
<text x="1190" y="588" font-family="'IBM Plex Mono',monospace" font-size="6.5" fill="#F59E0B" text-anchor="middle">TTL=step timeout</text>
<text x="1190" y="603" font-family="'IBM Plex Mono',monospace" font-size="7" fill="#334155" text-anchor="middle">deposit: built-artifact</text>
</g>
<!-- ========== STEP 10: Stage 2: install.nu (Stage 2, x=1310, yc=620) ========== -->
<g class="p10">
<circle cx="1310" cy="588" r="9" fill="#A5B4FC"/>
<text x="1310" y="592" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">10</text>
<rect x="1263" y="597" width="94" height="44" rx="5" fill="#A5B4FC" fill-opacity="0.12" stroke="#A5B4FC" stroke-width="1.5"/>
<text x="1310" y="613" font-family="'IBM Plex Mono',monospace" font-size="9" fill="#A5B4FC" text-anchor="middle" font-weight="500">install.nu</text>
<text x="1310" y="625" font-family="'Inter',sans-serif" font-size="8" fill="#64748B" text-anchor="middle">extracts: built-artifact</text>
<text x="1310" y="650" font-family="'IBM Plex Mono',monospace" font-size="7" fill="#334155" text-anchor="middle">deposit: installed</text>
</g>
<!-- ========== STEP 11: pipeline_run → SurrealDB (SurrealDB, x=1420, yc=700) ========== -->
<g class="p11">
<circle cx="1420" cy="668" r="9" fill="#A78BFA"/>
<text x="1420" y="672" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">11</text>
<rect x="1373" y="677" width="94" height="44" rx="5" fill="#A78BFA" fill-opacity="0.15" stroke="#A78BFA" stroke-width="1.5"/>
<text x="1420" y="693" font-family="'IBM Plex Mono',monospace" font-size="8.5" fill="#A78BFA" text-anchor="middle" font-weight="500">pipeline_run</text>
<text x="1420" y="706" font-family="'IBM Plex Mono',monospace" font-size="8" fill="#64748B" text-anchor="middle">status: success</text>
<text x="1420" y="727" font-family="'Inter',sans-serif" font-size="7.5" fill="#334155" text-anchor="middle">all steps persisted</text>
</g>
<!-- ========== STEP 12: emit result event (NATS diamond, x=1530, yc=220) ========== -->
<g class="nd p12">
<circle cx="1530" cy="178" r="9" fill="#22D3EE"/>
<text x="1530" y="182" font-family="'IBM Plex Mono',monospace" font-size="9" fill="white" text-anchor="middle" font-weight="700">12</text>
<polygon points="1530,185 1592,220 1530,255 1468,220" fill="#22D3EE" fill-opacity="0.12" stroke="#22D3EE" stroke-width="1.5"/>
<text x="1530" y="208" font-family="'IBM Plex Mono',monospace" font-size="8" fill="#22D3EE" text-anchor="middle">dev.crate.</text>
<text x="1530" y="221" font-family="'IBM Plex Mono',monospace" font-size="8" fill="#22D3EE" text-anchor="middle">provisioning-cli</text>
<text x="1530" y="234" font-family="'IBM Plex Mono',monospace" font-size="8" fill="#22D3EE" text-anchor="middle">.built</text>
<text x="1530" y="264" font-family="'Inter',sans-serif" font-size="7.5" fill="#334155" text-anchor="middle">duration_ms, status</text>
</g>
<!-- ========== FLOW ARROWS ========== -->
<!-- 1→2: git push triggers webhook (Developer lane →) -->
<line x1="255" y1="140" x2="292" y2="162" stroke="#F97316" stroke-width="1.5" stroke-dasharray="5,3" marker-end="url(#ao)" class="fd"/>
<!-- 2→3: NATS event received (cross-lane: NATS down to Orchestrator) -->
<line x1="330" y1="216" x2="330" y2="270" stroke="#6366F1" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#ai)"/>
<line x1="330" y1="299" x2="415" y2="299" stroke="#6366F1" stroke-width="1.5" stroke-dasharray="5,3" marker-end="url(#ai)" class="fds"/>
<!-- 3→4: Orchestrator requests NKey verify (cross-lane down to Auth) -->
<line x1="487" y1="299" x2="525" y2="365" stroke="#F59E0B" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#ag)"/>
<!-- 4→5: Auth ok, Orchestrator proceeds (cross-lane back up + forward) -->
<line x1="606" y1="385" x2="644" y2="299" stroke="#6366F1" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#ai)"/>
<!-- 5→6: ActionGraph built, Cedar authorize -->
<line x1="732" y1="300" x2="760" y2="365" stroke="#F59E0B" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#ag)"/>
<!-- 6→7: Auth permits, launch Stage 0 -->
<line x1="833" y1="398" x2="878" y2="445" stroke="#10B981" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#agr)"/>
<!-- 7→8: Stage 0 join → deposit to Orchestrator -->
<line x1="1038" y1="458" x2="1075" y2="323" stroke="#6366F1" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#ai)"/>
<!-- 8→9: Deposit done → Stage 1 -->
<line x1="1075" y1="323" x2="1153" y2="517" stroke="#818CF8" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#aib)"/>
<!-- 9→10: Stage 1 done → Stage 2 -->
<line x1="1227" y1="540" x2="1263" y2="617" stroke="#A5B4FC" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#ail)"/>
<!-- 10→11: Stage 2 done → SurrealDB ACK -->
<line x1="1310" y1="641" x2="1373" y2="693" stroke="#A78BFA" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#ap)"/>
<!-- 11→12: SurrealDB persisted → emit result NATS -->
<path d="M1420,677 L1420,220 L1474,220" fill="none" stroke="#22D3EE" stroke-width="1.5" stroke-dasharray="5,3" marker-end="url(#ac)" class="fds"/>
<!-- Orchestrator ↔ SurrealDB (bidirectional state) -->
<path d="M1075,323 Q1075,700 1373,700" fill="none" stroke="#A78BFA" stroke-width="1" stroke-dasharray="3,3" opacity="0.4"/>
<!-- ========== ROLLBACK PATH ========== -->
<path d="M1153,574 L800,574" fill="none" stroke="#F43F5E" stroke-width="2" stroke-dasharray="7,4" marker-end="url(#ar)" opacity="0.85"/>
<rect x="850" y="553" width="210" height="17" rx="3" fill="#F1F5F9" fill-opacity="0.95"/>
<text x="955" y="565" font-family="'IBM Plex Mono',monospace" font-size="7.5" fill="#F43F5E" text-anchor="middle">rollback path: compensate.nu in reverse order</text>
<!-- ========== LOGO above LEGEND ========== -->
<g transform="translate(10,15) scale(0.2)">
<g transform="translate(20,50)">
<rect x="-220" y="18" width="220" height="24" rx="5" fill="url(#hpLayerGrad)" opacity="0.7">
<animate attributeName="x" values="-220;0" dur="0.5s" fill="freeze" calcMode="spline" keySplines="0.25 0.1 0.25 1"/>
<animate attributeName="opacity" values="0.5;0.8;0.5" dur="3s" repeatCount="indefinite" begin="1.5s"/>
</rect>
<rect x="-220" y="88" width="220" height="24" rx="5" fill="url(#hpLayerGrad)">
<animate attributeName="x" values="-220;0" dur="0.5s" fill="freeze" begin="0.15s" calcMode="spline" keySplines="0.25 0.1 0.25 1"/>
</rect>
<rect x="-220" y="158" width="220" height="24" rx="5" fill="url(#hpLayerGrad)" opacity="0.7">
<animate attributeName="x" values="-220;0" dur="0.5s" fill="freeze" begin="0.3s" calcMode="spline" keySplines="0.25 0.1 0.25 1"/>
<animate attributeName="opacity" values="0.8;0.5;0.8" dur="3s" repeatCount="indefinite" begin="1.5s"/>
</rect>
<path d="M35 42 Q35 65 70 85 Q95 100 95 100" stroke="url(#hpFlowGrad)" stroke-width="3" fill="none" opacity="0" stroke-linecap="round"><animate attributeName="opacity" values="0;0.6" dur="0.3s" fill="freeze" begin="0.5s"/></path>
<path d="M185 42 Q185 65 150 85 Q125 100 125 100" stroke="url(#hpFlowGrad)" stroke-width="3" fill="none" opacity="0" stroke-linecap="round"><animate attributeName="opacity" values="0;0.6" dur="0.3s" fill="freeze" begin="0.6s"/></path>
<path d="M95 100 Q95 100 70 115 Q35 135 35 158" stroke="url(#hpFlowGrad)" stroke-width="3" fill="none" opacity="0" stroke-linecap="round"><animate attributeName="opacity" values="0;0.6" dur="0.3s" fill="freeze" begin="0.7s"/></path>
<path d="M125 100 Q125 100 150 115 Q185 135 185 158" stroke="url(#hpFlowGrad)" stroke-width="3" fill="none" opacity="0" stroke-linecap="round"><animate attributeName="opacity" values="0;0.6" dur="0.3s" fill="freeze" begin="0.8s"/></path>
<circle r="4" fill="#22D3EE" filter="url(#hpNodeGlow)"><animateMotion dur="1.2s" repeatCount="indefinite" begin="1.5s"><mpath href="#hpPathIn1"/></animateMotion><animate attributeName="opacity" values="1;0.5;0.2" dur="1.2s" repeatCount="indefinite" begin="1.5s"/></circle>
<circle r="4" fill="#22D3EE" filter="url(#hpNodeGlow)"><animateMotion dur="1.2s" repeatCount="indefinite" begin="1.8s"><mpath href="#hpPathIn2"/></animateMotion><animate attributeName="opacity" values="1;0.5;0.2" dur="1.2s" repeatCount="indefinite" begin="1.8s"/></circle>
<circle r="3" fill="#6366F1"><animateMotion dur="1.2s" repeatCount="indefinite" begin="2.1s"><mpath href="#hpPathOut1"/></animateMotion><animate attributeName="opacity" values="0.2;0.5;1" dur="1.2s" repeatCount="indefinite" begin="2.1s"/></circle>
<circle r="3" fill="#6366F1"><animateMotion dur="1.2s" repeatCount="indefinite" begin="2.4s"><mpath href="#hpPathOut2"/></animateMotion><animate attributeName="opacity" values="0.2;0.5;1" dur="1.2s" repeatCount="indefinite" begin="2.4s"/></circle>
<circle cx="35" cy="30" r="0" fill="#22D3EE" filter="url(#hpNodeGlow)"><animate attributeName="r" values="0;6" dur="0.2s" fill="freeze" begin="0.25s"/><animate attributeName="r" values="5;7;5" dur="2s" repeatCount="indefinite" begin="1.5s"/></circle>
<circle cx="185" cy="30" r="0" fill="#22D3EE" filter="url(#hpNodeGlow)"><animate attributeName="r" values="0;6" dur="0.2s" fill="freeze" begin="0.3s"/><animate attributeName="r" values="6;5;6" dur="2s" repeatCount="indefinite" begin="1.5s"/></circle>
<circle cx="35" cy="170" r="0" fill="#6366F1"><animate attributeName="r" values="0;6" dur="0.2s" fill="freeze" begin="0.4s"/><animate attributeName="r" values="5;7;5" dur="2s" repeatCount="indefinite" begin="1.8s"/></circle>
<circle cx="185" cy="170" r="0" fill="#6366F1"><animate attributeName="r" values="0;6" dur="0.2s" fill="freeze" begin="0.45s"/><animate attributeName="r" values="6;5;6" dur="2s" repeatCount="indefinite" begin="1.8s"/></circle>
<rect x="85" y="75" width="50" height="50" rx="9" fill="url(#hpProcessorGrad)" filter="url(#hpProcessorGlow)" opacity="0"><animate attributeName="opacity" values="0;1" dur="0.3s" fill="freeze" begin="0.45s"/></rect>
<rect x="97" y="87" width="26" height="26" rx="5" fill="#ffffff" opacity="0"><animate attributeName="opacity" values="0;0.95" dur="0.25s" fill="freeze" begin="0.55s"/></rect>
<rect x="101" y="96" width="4" height="8" rx="1" fill="#22D3EE" opacity="0"><animate attributeName="opacity" values="0;1" dur="0.15s" fill="freeze" begin="0.7s"/><animate attributeName="height" values="8;14;6;10;8" dur="0.6s" repeatCount="indefinite" begin="1.2s"/><animate attributeName="y" values="96;93;97;95;96" dur="0.6s" repeatCount="indefinite" begin="1.2s"/></rect>
<rect x="108" y="93" width="4" height="14" rx="1" fill="#06B6D4" opacity="0"><animate attributeName="opacity" values="0;1" dur="0.15s" fill="freeze" begin="0.75s"/><animate attributeName="height" values="14;8;12;16;14" dur="0.55s" repeatCount="indefinite" begin="1.25s"/><animate attributeName="y" values="93;96;94;92;93" dur="0.55s" repeatCount="indefinite" begin="1.25s"/></rect>
<rect x="115" y="95" width="4" height="10" rx="1" fill="#22D3EE" opacity="0"><animate attributeName="opacity" values="0;1" dur="0.15s" fill="freeze" begin="0.8s"/><animate attributeName="height" values="10;16;8;12;10" dur="0.5s" repeatCount="indefinite" begin="1.3s"/><animate attributeName="y" values="95;92;96;94;95" dur="0.5s" repeatCount="indefinite" begin="1.3s"/></rect>
<rect x="85" y="75" width="50" height="50" rx="9" fill="none" stroke="#22D3EE" stroke-width="1.5" opacity="0"><animate attributeName="x" values="85;70" dur="2s" repeatCount="indefinite" begin="1.5s"/><animate attributeName="y" values="75;60" dur="2s" repeatCount="indefinite" begin="1.5s"/><animate attributeName="width" values="50;80" dur="2s" repeatCount="indefinite" begin="1.5s"/><animate attributeName="height" values="50;80" dur="2s" repeatCount="indefinite" begin="1.5s"/><animate attributeName="rx" values="9;14" dur="2s" repeatCount="indefinite" begin="1.5s"/><animate attributeName="opacity" values="0.5;0" dur="2s" repeatCount="indefinite" begin="1.5s"/></rect>
</g>
<g clip-path="url(#hpTextReveal)">
<text x="280" y="175" font-family="'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-weight="700" font-size="72" fill="url(#hpShimmer)">Stratum<tspan fill="url(#hpProcessorGrad)">I</tspan>Ops</text>
</g>
<rect x="280" y="115" width="4" height="70" rx="2" fill="#22D3EE" opacity="0"><animate attributeName="opacity" values="0;1;1;0;0" dur="0.8s" fill="freeze" begin="1s" keyTimes="0;0.1;0.5;0.51;1"/><animate attributeName="x" values="280;980" dur="1s" fill="freeze" begin="1s" calcMode="spline" keySplines="0.25 0.1 0.25 1"/></rect>
<line x1="280" y1="195" x2="280" y2="195" stroke="url(#hpUnderlineGrad)" stroke-width="3" stroke-linecap="round" opacity="0"><animate attributeName="opacity" values="0;0.7" dur="0.1s" fill="freeze" begin="1.4s"/><animate attributeName="x2" values="280;750" dur="0.8s" fill="freeze" begin="1.4s" calcMode="spline" keySplines="0.25 0.1 0.25 1"/></line>
</g>
<!-- ========== LEGEND (y=760-800) ========== -->
<text x="192" y="770" font-family="'Inter',sans-serif" font-size="9" fill="#334155" font-weight="600">Legend:</text>
<rect x="240" y="759" width="11" height="11" rx="2" fill="#22D3EE" fill-opacity="0.25" stroke="#22D3EE" stroke-width="1"/>
<text x="256" y="769" font-family="'Inter',sans-serif" font-size="8.5" fill="#64748B">NATS events</text>
<rect x="335" y="759" width="11" height="11" rx="2" fill="#6366F1" fill-opacity="0.25" stroke="#6366F1" stroke-width="1"/>
<text x="351" y="769" font-family="'Inter',sans-serif" font-size="8.5" fill="#64748B">Orchestrator</text>
<rect x="434" y="759" width="11" height="11" rx="2" fill="#F59E0B" fill-opacity="0.25" stroke="#F59E0B" stroke-width="1"/>
<text x="450" y="769" font-family="'Inter',sans-serif" font-size="8.5" fill="#64748B">Auth (NKeys + Cedar)</text>
<rect x="578" y="759" width="11" height="11" rx="2" fill="#10B981" fill-opacity="0.25" stroke="#10B981" stroke-width="1"/>
<text x="594" y="769" font-family="'Inter',sans-serif" font-size="8.5" fill="#64748B">Execution (Stage 0)</text>
<rect x="726" y="759" width="11" height="11" rx="2" fill="#A78BFA" fill-opacity="0.25" stroke="#A78BFA" stroke-width="1"/>
<text x="742" y="769" font-family="'Inter',sans-serif" font-size="8.5" fill="#64748B">State (SurrealDB)</text>
<line x1="852" y1="765" x2="875" y2="765" stroke="#F43F5E" stroke-width="2" stroke-dasharray="5,3"/>
<text x="882" y="769" font-family="'Inter',sans-serif" font-size="8.5" fill="#64748B">Rollback path</text>
<text x="1588" y="775" font-family="'Inter',sans-serif" font-size="8" fill="#374151" text-anchor="end">StratumIOps · stratum-orchestrator</text>
<text x="1588" y="789" font-family="'IBM Plex Mono',monospace" font-size="7" fill="#374151" text-anchor="end">stratum-orchestrator · build pipeline</text>
</svg>