.nornir/assets/facett_adapter_flow.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1180 560" 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>
<marker id="arrow" markerWidth="9" markerHeight="9" refX="6.5" refY="4" orient="auto" markerUnits="userSpaceOnUse">
<path d="M0 0 L8 4 L0 8 Z" fill="#6fd3f5"/>
</marker>
<marker id="arrowd" markerWidth="9" markerHeight="9" refX="6.5" refY="4" orient="auto" markerUnits="userSpaceOnUse">
<path d="M0 0 L8 4 L0 8 Z" fill="#7fb0cc"/>
</marker>
</defs>
<!-- PCB substrate -->
<rect x="0" y="0" width="1180" height="560" fill="url(#board)"/>
<g stroke="#143a52" stroke-width="1">
<line x1="0" y1="76" x2="1180" y2="76"/>
<line x1="0" y1="512" x2="1180" y2="512"/>
</g>
<g fill="#16415c">
<circle cx="40" cy="40" r="2"/><circle cx="1140" cy="40" r="2"/>
<circle cx="40" cy="532" r="2"/><circle cx="1140" cy="532" r="2"/>
</g>
<!-- title -->
<text x="40" y="44" fill="#eaf6ff" font-size="22" font-weight="700">facett-adapter</text>
<text x="296" y="44" fill="#9fc9e0" font-size="15">knut's Spark/SDP <tspan fill="#bfe6ff" font-weight="700">ViewerState</tspan> → facett's engine-agnostic <tspan fill="#bfe6ff" font-weight="700">PipelineModel</tspan></text>
<!-- legend -->
<g font-size="12.5">
<line x1="828" y1="60" x2="868" y2="60" stroke="#6fd3f5" stroke-width="3"/>
<text x="876" y="64" fill="#cfe6f5">Spark adapter (ships now)</text>
<line x1="1050" y1="60" x2="1090" y2="60" stroke="#7fb0cc" stroke-width="2.5" stroke-dasharray="5 4"/>
<text x="1098" y="64" fill="#cfe6f5">future</text>
</g>
<!-- ===================== VIEWERSTATE (left, source) ===================== -->
<rect x="36" y="130" width="190" height="252" rx="12" fill="url(#chip)" stroke="#5aa6cf" stroke-width="2"/>
<text x="131" y="158" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">knut ViewerState</text>
<text x="131" y="177" fill="#9fc9e0" font-size="10.5" text-anchor="middle">Spark / SDP source</text>
<g font-family="'DejaVu Sans Mono', monospace" font-size="9.5">
<rect x="56" y="190" width="150" height="34" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.4"/>
<text x="66" y="204" fill="#bfe6ff">DataflowGraph</text>
<text x="66" y="217" fill="#86b3cc" font-size="8.5">datasets + flows</text>
<rect x="56" y="230" width="150" height="34" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.4"/>
<text x="66" y="244" fill="#bfe6ff">NodeStatus / node</text>
<text x="66" y="257" fill="#86b3cc" font-size="8.5">idle / running / done / failed</text>
<rect x="56" y="270" width="150" height="34" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.4"/>
<text x="66" y="284" fill="#bfe6ff">event log</text>
<text x="66" y="297" fill="#86b3cc" font-size="8.5">error events per dataset</text>
<rect x="56" y="310" width="150" height="34" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.4"/>
<text x="66" y="324" fill="#bfe6ff">ExecutionProgress</text>
<text x="66" y="337" fill="#86b3cc" font-size="8.5">Spark job task counts</text>
</g>
<text x="131" y="366" fill="#7fa8c4" font-size="9" text-anchor="middle">facett never leaks into the public API</text>
<rect x="226" y="252" width="6" height="8" fill="#6fd3f5"/>
<!-- ViewerState -> adapter -->
<g stroke="#6fd3f5" stroke-width="3" fill="none">
<path d="M232 256 H300" marker-end="url(#arrow)"/>
</g>
<!-- ===================== SPARK ADAPTER ===================== -->
<rect x="306" y="156" width="206" height="200" rx="12" fill="url(#rail)" stroke="#7fc8ea" stroke-width="2.5"/>
<text x="409" y="184" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">SparkPipelineAdapter</text>
<text x="409" y="202" fill="#9fc9e0" font-size="10" text-anchor="middle">[feature facett-adapter]</text>
<text x="409" y="222" fill="#bfe6ff" font-size="10" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">to_pipeline_model()</text>
<g font-family="'DejaVu Sans Mono', monospace" font-size="9">
<rect x="326" y="234" width="166" height="22" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.2"/>
<text x="336" y="249" fill="#bfe6ff">Dataset → Stage (+kind)</text>
<rect x="326" y="260" width="166" height="22" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.2"/>
<text x="336" y="275" fill="#bfe6ff">read→target → PipelineEdge</text>
<rect x="326" y="286" width="166" height="22" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.2"/>
<text x="336" y="301" fill="#bfe6ff">Progress → PipelineMetrics</text>
</g>
<text x="409" y="326" fill="#86b3cc" font-size="9" text-anchor="middle">impl facett's PipelineAdapter seam</text>
<text x="409" y="342" fill="#7fa8c4" font-size="9" text-anchor="middle">ViewerState impls it too</text>
<rect x="512" y="252" width="6" height="8" fill="#6fd3f5"/>
<!-- adapter -> model -->
<g stroke="#6fd3f5" stroke-width="3" fill="none">
<path d="M518 256 H586" marker-end="url(#arrow)"/>
</g>
<!-- ===================== PIPELINEMODEL ===================== -->
<rect x="592" y="140" width="206" height="232" rx="12" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2.5"/>
<text x="695" y="168" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">PipelineModel</text>
<text x="695" y="186" fill="#9fc9e0" font-size="10.5" text-anchor="middle">normalized, engine-agnostic</text>
<g font-family="'DejaVu Sans Mono', monospace" font-size="9.5">
<rect x="612" y="198" width="166" height="34" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.4"/>
<text x="622" y="212" fill="#bfe6ff">Stage</text>
<text x="622" y="225" fill="#86b3cc" font-size="8.5">id name status kind error</text>
<rect x="612" y="238" width="166" height="34" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.4"/>
<text x="622" y="252" fill="#bfe6ff">PipelineEdge</text>
<text x="622" y="265" fill="#86b3cc" font-size="8.5">from to label</text>
<rect x="612" y="278" width="166" height="34" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.4"/>
<text x="622" y="292" fill="#bfe6ff">PipelineMetrics</text>
<text x="622" y="305" fill="#86b3cc" font-size="8.5">fraction tasks bytes rows</text>
</g>
<text x="695" y="334" fill="#86b3cc" font-size="9" text-anchor="middle">StageStatus: Idle Running Done Failed</text>
<text x="695" y="350" fill="#7fa8c4" font-size="9" text-anchor="middle">per-stage StageMetrics.rows = real rows_written; elapsed_s = measured</text>
<rect x="798" y="252" width="6" height="8" fill="#6fd3f5"/>
<!-- model -> view -->
<g stroke="#6fd3f5" stroke-width="3" fill="none">
<path d="M804 256 H872" marker-end="url(#arrow)"/>
</g>
<!-- ===================== PIPELINEVIEW ===================== -->
<rect x="878" y="176" width="170" height="160" rx="11" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2"/>
<text x="963" y="204" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">facett PipelineView</text>
<text x="963" y="222" fill="#9fc9e0" font-size="10" text-anchor="middle">reusable egui viewer</text>
<rect x="898" y="234" width="130" height="30" rx="5" fill="url(#slot)" stroke="#6fd3f5" stroke-width="1.4"/>
<text x="963" y="253" fill="#bfe6ff" font-size="9.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">from_adapter(&vs)</text>
<text x="963" y="284" fill="#86b3cc" font-size="9.5" text-anchor="middle">draws any engine's</text>
<text x="963" y="298" fill="#86b3cc" font-size="9.5" text-anchor="middle">PipelineModel uniformly</text>
<text x="963" y="320" fill="#7fa8c4" font-size="9" text-anchor="middle">one viewer, many adapters</text>
<!-- ===================== FUTURE ADAPTERS (dashed, below adapter) ===================== -->
<rect x="306" y="404" width="206" height="78" rx="11" fill="#0a2032" stroke="#3c6a86" stroke-width="1.4" stroke-dasharray="3 3"/>
<text x="409" y="428" fill="#bfe6ff" font-size="12.5" font-weight="700" text-anchor="middle">future adapters</text>
<text x="409" y="447" fill="#7fa8c4" font-size="10" text-anchor="middle">Flink · Beam · skade-streaming</text>
<text x="409" y="465" fill="#7fa8c4" font-size="9.5" text-anchor="middle">"just another impl PipelineAdapter" (deferred)</text>
<!-- future adapters -> same PipelineModel (dashed) -->
<g stroke="#7fb0cc" stroke-width="2.5" fill="none" stroke-dasharray="5 4">
<path d="M512 440 H560 V300 H586" marker-end="url(#arrowd)"/>
</g>
<!-- ===================== GROUND RAIL ===================== -->
<rect x="36" y="452" width="270" height="56" rx="10" fill="url(#rail)" stroke="#7fc8ea" stroke-width="2"/>
<text x="60" y="479" fill="#dff1ff" font-size="13" font-weight="700">back-compat</text>
<text x="60" y="497" fill="#7fa8c4" font-size="9.5" font-family="'DejaVu Sans Mono', monospace">default build unchanged</text>
<rect x="528" y="452" width="612" height="56" rx="10" fill="url(#rail)" stroke="#7fc8ea" stroke-width="2"/>
<g fill="#0a1c2c" stroke="#7fc8ea" stroke-width="1.5">
<circle cx="566" cy="480" r="4"/><circle cx="1040" cy="480" r="4"/>
</g>
<text x="588" y="479" fill="#dff1ff" font-size="13" font-weight="700">law</text>
<text x="640" y="475" fill="#bcd9ec" font-size="11">one adapter per engine, one viewer for all · facett model types never cross into knut's API</text>
<text x="640" y="496" fill="#7fa8c4" font-size="10" font-family="'DejaVu Sans Mono', monospace">facett-adapter = ["dep:facett-pipeline"] · SparkPipelineAdapter is the only impl shipping now</text>
</svg>