.nornir/assets/release_events_flow.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1180 680" font-family="'DejaVu Sans', 'Segoe UI', sans-serif">
<defs>
<linearGradient id="board" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#0b1c2e"/>
<stop offset="1" stop-color="#06111d"/>
</linearGradient>
<linearGradient id="chip" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#14384f"/>
<stop offset="1" stop-color="#0e2a3e"/>
</linearGradient>
<linearGradient id="rail" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#173d57"/>
<stop offset="1" stop-color="#0c2438"/>
</linearGradient>
<linearGradient id="slot" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#123448"/>
<stop offset="1" stop-color="#0a2032"/>
</linearGradient>
<linearGradient id="ice" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#e4f5ff" stop-opacity="0.95"/>
<stop offset="0.55" stop-color="#bfe6ff" stop-opacity="0.6"/>
<stop offset="1" stop-color="#bfe6ff" stop-opacity="0.08"/>
</linearGradient>
</defs>
<!-- PCB substrate -->
<rect x="0" y="0" width="1180" height="680" fill="url(#board)"/>
<g stroke="#143a52" stroke-width="1">
<line x1="0" y1="76" x2="1180" y2="76"/>
<line x1="0" y1="632" x2="1180" y2="632"/>
</g>
<g fill="#16415c">
<circle cx="40" cy="40" r="2"/><circle cx="1140" cy="40" r="2"/>
<circle cx="40" cy="652" r="2"/><circle cx="1140" cy="652" r="2"/>
</g>
<!-- title -->
<text x="40" y="44" fill="#eaf6ff" font-size="22" font-weight="700">release_events</text>
<text x="244" y="44" fill="#9fc9e0" font-size="15">every op boundary becomes a row → the warehouse holds the <tspan fill="#bfe6ff" font-weight="700">live op DAG</tspan></text>
<!-- legend -->
<g font-size="12.5">
<line x1="744" y1="60" x2="784" y2="60" stroke="#6fd3f5" stroke-width="3"/>
<text x="792" y="64" fill="#cfe6f5">op → emit (one row)</text>
<line x1="946" y1="60" x2="986" y2="60" stroke="#7fb0cc" stroke-width="2.5" stroke-dasharray="5 4"/>
<text x="994" y="64" fill="#cfe6f5">best-effort (non-fatal)</text>
</g>
<!-- ===================== DRIVERS (left) ===================== -->
<rect x="36" y="120" width="158" height="120" rx="10" fill="url(#chip)" stroke="#5aa6cf" stroke-width="2"/>
<text x="115" y="150" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">nornir release run</text>
<text x="115" y="170" fill="#9fc9e0" font-size="10.5" text-anchor="middle">fat-CLI path</text>
<text x="115" y="200" fill="#86b3cc" font-size="10.5" text-anchor="middle">walks the build order,</text>
<text x="115" y="215" fill="#86b3cc" font-size="10.5" text-anchor="middle">deps-first by seq</text>
<rect x="194" y="174" width="6" height="8" fill="#6fd3f5"/>
<rect x="36" y="262" width="158" height="120" rx="10" fill="url(#chip)" stroke="#5aa6cf" stroke-width="2"/>
<text x="115" y="292" fill="#eaf6ff" font-size="14" font-weight="700" text-anchor="middle">run_pipeline</text>
<text x="115" y="311" fill="#9fc9e0" font-size="10.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">release::pipeline</text>
<text x="115" y="333" fill="#86b3cc" font-size="10.5" text-anchor="middle">library / server path</text>
<text x="115" y="348" fill="#86b3cc" font-size="10.5" text-anchor="middle">(nornir-server RPC)</text>
<rect x="194" y="316" width="6" height="8" fill="#6fd3f5"/>
<!-- drivers -> ops bus -->
<g stroke="#6fd3f5" stroke-width="3" fill="none">
<path d="M194 178 H236"/>
<path d="M194 320 H236"/>
</g>
<g fill="#bfecff"><circle cx="236" cy="178" r="4"/><circle cx="236" cy="320" r="4"/></g>
<text x="115" y="404" fill="#7fa8c4" font-size="10.5" text-anchor="middle">both drive one emitter</text>
<!-- ===================== PER-COMPONENT OPS ===================== -->
<rect x="236" y="104" width="208" height="300" rx="12" fill="url(#rail)" stroke="#7fc8ea" stroke-width="2"/>
<text x="340" y="130" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">per-component ops</text>
<text x="340" y="148" fill="#9fc9e0" font-size="10.5" text-anchor="middle">component × op × phase</text>
<g font-family="'DejaVu Sans Mono', monospace" font-size="11.5">
<rect x="258" y="162" width="164" height="32" rx="6" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.6"/>
<text x="272" y="182" fill="#bfe6ff">test</text>
<text x="408" y="182" fill="#86b3cc" font-size="9.5" text-anchor="end">start · end</text>
<circle cx="430" cy="178" r="4" fill="#6fd3f5"/>
<rect x="258" y="202" width="164" height="32" rx="6" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.6"/>
<text x="272" y="222" fill="#bfe6ff">bench</text>
<text x="408" y="222" fill="#86b3cc" font-size="9.5" text-anchor="end">start · end</text>
<circle cx="430" cy="218" r="4" fill="#6fd3f5"/>
<rect x="258" y="242" width="164" height="32" rx="6" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.6"/>
<text x="272" y="262" fill="#bfe6ff">gate</text>
<text x="408" y="262" fill="#86b3cc" font-size="9.5" text-anchor="end">end</text>
<circle cx="430" cy="258" r="4" fill="#6fd3f5"/>
<rect x="258" y="282" width="164" height="32" rx="6" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.6"/>
<text x="272" y="302" fill="#bfe6ff">publish</text>
<text x="408" y="302" fill="#86b3cc" font-size="9.5" text-anchor="end">start · end</text>
<circle cx="430" cy="298" r="4" fill="#6fd3f5"/>
<rect x="258" y="322" width="164" height="32" rx="6" fill="#0a2032" stroke="#3c6a86" stroke-width="1.4" stroke-dasharray="3 3"/>
<text x="272" y="342" fill="#7fa8c4">test/bench</text>
<text x="408" y="342" fill="#7fa8c4" font-size="9.5" text-anchor="end">skip</text>
</g>
<text x="340" y="378" fill="#86b3cc" font-size="10" text-anchor="middle">run/start … run/end brackets the whole run</text>
<text x="340" y="394" fill="#7fa8c4" font-size="10" text-anchor="middle">fail row carries where it stalled</text>
<!-- ops -> emitter -->
<g stroke="#6fd3f5" stroke-width="3" fill="none">
<path d="M444 178 H468 V240 H500"/>
<path d="M444 218 H482 V244 H500"/>
<path d="M444 258 H500"/>
<path d="M444 298 H482 V272 H500"/>
</g>
<circle cx="500" cy="258" r="5" fill="#bfecff"/>
<!-- ===================== EMITTER (the mint) ===================== -->
<rect x="500" y="178" width="170" height="160" rx="11" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2.5"/>
<text x="585" y="206" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">ReleaseEventEmitter</text>
<text x="585" y="226" fill="#9fc9e0" font-size="10.5" text-anchor="middle">holds run_id + shared</text>
<text x="585" y="240" fill="#9fc9e0" font-size="10.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">Arc<AtomicI64> seq</text>
<text x="585" y="266" fill="#bfe6ff" font-size="11" text-anchor="middle" font-weight="700">mints one row / boundary</text>
<text x="585" y="288" fill="#86b3cc" font-size="10" text-anchor="middle">run_id · seq · ts · component</text>
<text x="585" y="302" fill="#86b3cc" font-size="10" text-anchor="middle">op · phase · status · detail</text>
<text x="585" y="316" fill="#a9d8ee" font-size="10" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">depends_on[] · elapsed_ms</text>
<rect x="494" y="248" width="6" height="8" fill="#6fd3f5"/>
<rect x="670" y="248" width="6" height="8" fill="#6fd3f5"/>
<!-- dep-graph feed into depends_on -->
<path d="M585 178 V150 H236" stroke="#7fb0cc" stroke-width="2" fill="none" stroke-dasharray="4 3"/>
<text x="430" y="142" fill="#86b3cc" font-size="10" text-anchor="middle">depends_on ← dep-graph producers</text>
<!-- emitter -> append -->
<g stroke="#6fd3f5" stroke-width="3" fill="none">
<path d="M670 252 H720"/>
</g>
<circle cx="720" cy="252" r="4" fill="#bfecff"/>
<!-- ===================== APPEND (best-effort) ===================== -->
<rect x="720" y="200" width="150" height="108" rx="10" fill="url(#chip)" stroke="#5aa6cf" stroke-width="2"/>
<text x="795" y="228" fill="#eaf6ff" font-size="13.5" font-weight="700" text-anchor="middle">append_</text>
<text x="795" y="245" fill="#eaf6ff" font-size="13.5" font-weight="700" text-anchor="middle">release_events</text>
<text x="795" y="266" fill="#9fc9e0" font-size="10" text-anchor="middle">one Iceberg snapshot</text>
<text x="795" y="280" fill="#9fc9e0" font-size="10" text-anchor="middle">per emit</text>
<text x="795" y="298" fill="#7fa8c4" font-size="9.5" text-anchor="middle">error logged + swallowed</text>
<rect x="714" y="248" width="6" height="8" fill="#6fd3f5"/>
<rect x="870" y="248" width="6" height="8" fill="#6fd3f5"/>
<!-- non-fatal drop rail -->
<path d="M795 308 V356 H700" stroke="#7fb0cc" stroke-width="2.5" fill="none" stroke-dasharray="6 4"/>
<text x="690" y="360" fill="#86b3cc" font-size="10" text-anchor="end">dropped row never aborts a green release</text>
<!-- append -> warehouse rail -->
<g stroke="#6fd3f5" stroke-width="3" fill="none">
<path d="M870 252 H920 V560"/>
</g>
<!-- ===================== READ -> VIZ (top right) ===================== -->
<rect x="940" y="120" width="200" height="150" rx="11" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2.5"/>
<text x="1040" y="148" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">viz Release Ops</text>
<text x="1040" y="167" fill="#9fc9e0" font-size="10.5" text-anchor="middle">one swim-lane / component</text>
<text x="1040" y="186" fill="#bfe6ff" font-size="10.5" text-anchor="middle">nodes by seq · colour by status</text>
<text x="1040" y="205" fill="#86b3cc" font-size="10" text-anchor="middle">cross-lane edges drawn</text>
<text x="1040" y="219" fill="#86b3cc" font-size="10" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">straight from depends_on</text>
<text x="1040" y="240" fill="#a9d8ee" font-size="10" text-anchor="middle">stalled lane is obvious</text>
<text x="1040" y="256" fill="#7fa8c4" font-size="9.5" text-anchor="middle">poll = live DAG fills in</text>
<!-- read path: query_release_events + render -->
<g font-family="'DejaVu Sans Mono', monospace" font-size="10.5">
<text x="1040" y="306" fill="#a9d8ee" text-anchor="middle">query_release_events</text>
</g>
<text x="1040" y="324" fill="#86b3cc" font-size="9.5" text-anchor="middle">scope: Run | Repo | All · sort by (run_id, seq)</text>
<text x="1040" y="340" fill="#86b3cc" font-size="9.5" text-anchor="middle">render_topo / rows_to_json</text>
<text x="1040" y="356" fill="#7fa8c4" font-size="9.5" text-anchor="middle">nornir release events — lock-tolerant read</text>
<!-- warehouse -> viz lead (read up) -->
<g stroke="#6fd3f5" stroke-width="3" fill="none">
<path d="M1040 560 V270"/>
</g>
<circle cx="1040" cy="270" r="4" fill="#bfecff"/>
<text x="1056" y="430" fill="#86b3cc" font-size="10">read</text>
<!-- ===================== WAREHOUSE GROUND RAIL ===================== -->
<rect x="236" y="552" width="904" height="56" rx="10" fill="url(#rail)" stroke="#7fc8ea" stroke-width="2"/>
<g fill="#0a1c2c" stroke="#7fc8ea" stroke-width="1.5">
<circle cx="280" cy="580" r="4"/><circle cx="560" cy="580" r="4"/>
<circle cx="820" cy="580" r="4"/><circle cx="1040" cy="580" r="4"/>
</g>
<text x="306" y="579" fill="#dff1ff" font-size="15" font-weight="700">release_events</text>
<text x="466" y="575" fill="#bcd9ec" font-size="11.5">append-only Iceberg table — the structural twin of the funnel DAG</text>
<text x="466" y="596" fill="#7fa8c4" font-size="10.5" font-family="'DejaVu Sans Mono', monospace">run_id · seq · component · op · phase · status · depends_on[] · elapsed_ms</text>
</svg>