<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Thorin Tabor</title><link href="https://tmtabor.io/" rel="alternate"/><link href="https://tmtabor.io/feeds/all.atom.xml" rel="self"/><id>https://tmtabor.io/</id><updated>2026-07-28T00:00:00-07:00</updated><entry><title>Building the GenePattern Module Toolkit</title><link href="https://tmtabor.io/blog/genepattern-module-toolkit/" rel="alternate"/><published>2026-07-28T00:00:00-07:00</published><updated>2026-07-28T00:00:00-07:00</updated><author><name>Thorin Tabor</name></author><id>tag:tmtabor.io,2026-07-28:/blog/genepattern-module-toolkit/</id><summary type="html">&lt;p&gt;How a hierarchical multi-agent pipeline increased GenePattern module production fivefold.&lt;/p&gt;</summary><content type="html">&lt;p&gt;It's five o'clock on a Wednesday, and I have twelve tabs open across two monitors. Four of them are Google searches. One is a Biostars thread from a few months back that I've had bookmarked for three days now, because it's the closest thing anyone has written to what I'm trying to understand. Another is a conversation with an AI assistant that's now twenty messages deep, patiently walking me through spatial transcriptomic gradients like a TA answering the same question for the fifth time this semester.&lt;/p&gt;
&lt;blockquote class="float-right w-[300px] ml-8 mb-4 pl-5 border-l-[3px] border-gray-400 dark:border-gray-600
         font-serif text-xl leading-snug
         max-[700px]:float-none max-[700px]:w-auto max-[700px]:ml-0" aria-hidden="true"&gt;
  It's just me, the documentation and however much of Biostars I can absorb before I have to go make dinner.
&lt;/blockquote&gt;

&lt;p&gt;I'm not a biologist. I'm a software engineer by training. And I have been assigned to integrate the &lt;a href="https://github.com/FridleyLab/spatialGE"&gt;spatialGE&lt;/a&gt; suite of tools into GenePattern, a bioinformatics platform used by researchers who mostly just want their data to turn into an answer to their scientific inquiries.&lt;/p&gt;
&lt;p&gt;This isn't a task I can fake my way through. spatialGE consists of a non-linear workflow with at least nine distinct steps and speaks two separate data formats. The modules I'm building have to work together as a coherent, containerized, multi-step pipeline instead of nine stand-alone scripts that happen to share a folder. Getting that right means actually understanding when a researcher would reach for enrichment versus differential expression versus one of half a dozen normalization schemes, because if I get that wrong, the module is wrong no matter how clean the code underneath it is.&lt;/p&gt;
&lt;p&gt;The people who wrote spatialGE are not in the room. They're not on Slack. As far as I can tell they're two time zones and a very long email chain away, and I've decided not to bother them until I've exhausted every other option. So it's just me, the documentation and however much of Biostars I can absorb before I have to go make dinner.&lt;/p&gt;
&lt;p&gt;This isn't really a story about spatial transcriptomics, though it starts there. &lt;a href="https://www.genepattern.org/"&gt;GenePattern&lt;/a&gt; has been around since the early 2000s and the idea behind it has aged well: researchers don't want to spend their afternoons untangling dependency trees or memorizing the command-line flags for a tool some grad student wrote eight years ago. The platform takes hundreds of independently developed bioinformatics tools, most written by academic labs with no particular interest in software engineering practices, and wraps each one in a user-friendly web form. It's a translation layer between "here is a tool that does interesting science" and "here is something a non-programmer can actually run." &lt;/p&gt;
&lt;p&gt;The catch is that every one of those tools has to be integrated by hand: someone sits down with the documentation, or with the scientists who built it if they're lucky enough to get a response, and figures out which parameters actually matter, what the true dependencies are, how to wrap it in a containerized environment and how to test that all of it works. &lt;/p&gt;
&lt;p&gt;For spatialGE, that someone was me, and by the time this story is over, that someone was going to become a pipeline of AI agents that eventually pushed our module output up by roughly 500 percent.&lt;/p&gt;
&lt;h2&gt;The toolkit gets its green light&lt;/h2&gt;
&lt;p&gt;We were a small team with a long backlog. Every new tool integration was traditionally a multi-day undertaking: read the docs, talk to the lab if you could, write the wrapper, write the manifest, write the tests and hope you haven't missed an edge case a scientist would find obvious.&lt;/p&gt;
&lt;p&gt;I'd just come off a different corner of the GenePattern ecosystem: a project called GenePattern Copilot, which was an agentic assistant that could answer platform and genomics questions, as well as execute tasks on a user's behalf. This meant agentic AI was already on my mind. So a week into teaching myself spatial transcriptomics by trial and Biostars, I had the thought that eventually occurs to every engineer stuck doing something repetitive and well-specified: &lt;em&gt;there has to be a better way to do it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I sketched the architecture on my whiteboard, not a single model doing everything end to end, but a hierarchy: an orchestrator agent at the top, and underneath it, a set of agents each responsible for producing one specific artifact a GenePattern module needs. I brought the sketch to my boss. It got the green light, and the GenePattern Module Toolkit was born.&lt;/p&gt;
&lt;p&gt;The idea was simple to state and much harder to build. Point the toolkit at a tool's documentation and its git repository, optionally tell it which user stories or features to prioritize, and let it do the work. What comes out the other end is a module a human can QA in an afternoon instead of building from scratch over three days.&lt;/p&gt;
&lt;p&gt;&lt;svg width="100%" viewBox="0 0 680 250" role="img" style="" xmlns="http://www.w3.org/2000/svg"&gt;&lt;title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"&gt;Before and after the Module Toolkit&lt;/title&gt;&lt;desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"&gt;Before: manual integration takes about three days per module. After: the Module Toolkit drafts every artifact, a human QAs it in an afternoon, and module production increases roughly 500 percent.&lt;/desc&gt;
&lt;defs&gt;&lt;marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"&gt;&lt;path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/marker&gt;&lt;/defs&gt;
&lt;line x1="320" y1="130" x2="360" y2="130" marker-end="url(#arrow)" style="fill:none;stroke:rgb(137, 135, 129);color:rgb(11, 11, 11);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/&gt;
&lt;g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"&gt;&lt;rect x="60" y="50" width="260" height="160" rx="16" stroke-width="0.5" style="fill:rgb(250, 236, 231);stroke:rgb(153, 60, 29);color:rgb(11, 11, 11);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/&gt;
&lt;text x="190" y="80" text-anchor="middle" dominant-baseline="central" style="fill:rgb(113, 43, 19);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:central"&gt;Manual integration&lt;/text&gt;
&lt;text x="190" y="110" text-anchor="middle" dominant-baseline="central" style="fill:rgb(153, 60, 29);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:central"&gt;Read docs, wait on the lab&lt;/text&gt;
&lt;text x="190" y="132" text-anchor="middle" dominant-baseline="central" style="fill:rgb(153, 60, 29);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:central"&gt;Hand-write every artifact&lt;/text&gt;
&lt;text x="190" y="156" text-anchor="middle" dominant-baseline="central" style="fill:rgb(113, 43, 19);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:central"&gt;~3 days per module&lt;/text&gt;&lt;/g&gt;
&lt;g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"&gt;&lt;rect x="360" y="50" width="260" height="160" rx="16" stroke-width="0.5" style="fill:rgb(225, 245, 238);stroke:rgb(15, 110, 86);color:rgb(11, 11, 11);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/&gt;
&lt;text x="490" y="80" text-anchor="middle" dominant-baseline="central" style="fill:rgb(8, 80, 65);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:central"&gt;Module toolkit&lt;/text&gt;
&lt;text x="490" y="110" text-anchor="middle" dominant-baseline="central" style="fill:rgb(15, 110, 86);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:central"&gt;Toolkit drafts every artifact&lt;/text&gt;
&lt;text x="490" y="132" text-anchor="middle" dominant-baseline="central" style="fill:rgb(15, 110, 86);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:central"&gt;Human QAs in an afternoon&lt;/text&gt;
&lt;text x="490" y="156" text-anchor="middle" dominant-baseline="central" style="fill:rgb(8, 80, 65);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&amp;quot;Anthropic Sans&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:central"&gt;~500% more modules shipped&lt;/text&gt;&lt;/g&gt;
&lt;/svg&gt;&lt;/p&gt;
&lt;h2&gt;One orchestrator, eight agents&lt;/h2&gt;
&lt;p&gt;Here's roughly what the toolkit looked like once it settled into a stable shape. At the top sits an orchestrator, managing a pipeline of eight subagents. The first two that get called are a researcher agent, which pulls documentation and context from online sources, and a planner agent, whose entire job is making sure every other agent is working from the same set of assumptions. Execution is then passed to a pipeline of six artifact agents, each responsible for a single component: manifest, wrapper script, parameter groups, documentation, tests and Dockerfile.&lt;/p&gt;
&lt;p&gt;The artifact agents use a pattern called chain of verification, though at the time I was just calling it "check the thing before moving to the next thing." Each agent generates its artifact. A linter script then runs against that artifact and checks whether it's actually valid. If it passes, the pipeline moves to the next agent. If it fails, the error gets escalated to the orchestrator, which has to make a judgment call: is this a bad run of this one artifact, or is it symptomatic of an upstream failure, something an earlier agent produced? If it's upstream, the orchestrator kicks the pipeline back to that earlier step and reruns from there. This is gated by a maximum number of iterations before it gives up and flags the run for human evaluation.&lt;/p&gt;
&lt;p&gt;&lt;svg id="export-svg" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 1228.0999755859375px;" viewBox="0 0 1228.0999755859375 566" role="graphics-document document" aria-roledescription="flowchart-v2"&gt;&lt;style xmlns="http://www.w3.org/1999/xhtml"&gt;p {margin: 0;}&lt;/style&gt;&lt;style&gt;#export-svg{font-family:arial,sans-serif;font-size:14px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#export-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#export-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#export-svg .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#export-svg .error-text{fill:rgb(8.5000000002, 5.7500000001, 0);stroke:rgb(8.5000000002, 5.7500000001, 0);}#export-svg .edge-thickness-normal{stroke-width:1px;}#export-svg .edge-thickness-thick{stroke-width:3.5px;}#export-svg .edge-pattern-solid{stroke-dasharray:0;}#export-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#export-svg .edge-pattern-dashed{stroke-dasharray:3;}#export-svg .edge-pattern-dotted{stroke-dasharray:2;}#export-svg .marker{fill:#0b0b0b;stroke:#0b0b0b;}#export-svg .marker.cross{stroke:#0b0b0b;}#export-svg svg{font-family:arial,sans-serif;font-size:14px;}#export-svg p{margin:0;}#export-svg .label{font-family:arial,sans-serif;color:#333;}#export-svg .cluster-label text{fill:rgb(8.5000000002, 5.7500000001, 0);}#export-svg .cluster-label span{color:rgb(8.5000000002, 5.7500000001, 0);}#export-svg .cluster-label span p{background-color:transparent;}#export-svg .label text,#export-svg span{fill:#333;color:#333;}#export-svg .node rect,#export-svg .node circle,#export-svg .node ellipse,#export-svg .node polygon,#export-svg .node path{fill:#fff4dd;stroke:hsl(40.5882352941, 60%, 83.3333333333%);stroke-width:1px;}#export-svg .rough-node .label text,#export-svg .node .label text,#export-svg .image-shape .label,#export-svg .icon-shape .label{text-anchor:middle;}#export-svg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#export-svg .rough-node .label,#export-svg .node .label,#export-svg .image-shape .label,#export-svg .icon-shape .label{text-align:center;}#export-svg .node.clickable{cursor:pointer;}#export-svg .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#export-svg .arrowheadPath{fill:#0b0b0b;}#export-svg .edgePath .path{stroke:#0b0b0b;stroke-width:1px;}#export-svg .flowchart-link{stroke:#0b0b0b;fill:none;}#export-svg .edgeLabel{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#export-svg .edgeLabel p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);}#export-svg .edgeLabel rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#export-svg .labelBkg{background-color:rgba(243.9999999999, 220.9999999998, 255, 0.5);}#export-svg .cluster rect{fill:#FFFFF8;stroke:hsl(220.5882352941, 60%, 88.3333333333%);stroke-width:1px;}#export-svg .cluster text{fill:rgb(8.5000000002, 5.7500000001, 0);}#export-svg .cluster span{color:rgb(8.5000000002, 5.7500000001, 0);}#export-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:arial,sans-serif;font-size:12px;background:hsl(220.5882352941, 100%, 98.3333333333%);border:1px solid hsl(220.5882352941, 60%, 88.3333333333%);border-radius:2px;pointer-events:none;z-index:100;}#export-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#export-svg rect.text{fill:none;stroke-width:0;}#export-svg .icon-shape,#export-svg .image-shape{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#export-svg .icon-shape p,#export-svg .image-shape p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);padding:2px;}#export-svg .icon-shape .label rect,#export-svg .image-shape .label rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#export-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#export-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#export-svg .node .neo-node{stroke:hsl(40.5882352941, 60%, 83.3333333333%);}#export-svg [data-look="neo"].node rect,#export-svg [data-look="neo"].cluster rect,#export-svg [data-look="neo"].node polygon{stroke:url(#export-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#export-svg [data-look="neo"].swimlane.cluster rect{filter:none;}#export-svg [data-look="neo"].node path{stroke:url(#export-svg-gradient);stroke-width:1px;}#export-svg [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#export-svg [data-look="neo"].node .neo-line path{stroke:hsl(40.5882352941, 60%, 83.3333333333%);filter:none;}#export-svg [data-look="neo"].node circle{stroke:url(#export-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#export-svg [data-look="neo"].node circle .state-start{fill:#000000;}#export-svg [data-look="neo"].icon-shape .icon{fill:url(#export-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#export-svg [data-look="neo"].icon-shape .icon-neo path{stroke:url(#export-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#export-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id="export-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="7.75" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"&gt;&lt;path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0px; stroke-dasharray: 1px, 0px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="4" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"&gt;&lt;polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0px; stroke-dasharray: 1px, 0px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"&gt;&lt;path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0px; stroke-dasharray: 1px, 0px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"&gt;&lt;polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0px; stroke-dasharray: 1px, 0px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="10.75" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0px; stroke-dasharray: 1px, 0px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="0" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0px; stroke-dasharray: 1px, 0px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0px; stroke-dasharray: 1px, 0px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0px; stroke-dasharray: 1px, 0px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5px; stroke-dasharray: 1px, 0px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5px;"/&gt;&lt;/marker&gt;&lt;marker id="export-svg_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5px; stroke-dasharray: 1px, 0px;"/&gt;&lt;/marker&gt;&lt;g class="root"&gt;&lt;g class="clusters"/&gt;&lt;g class="edgePaths"&gt;&lt;path d="M480.52809223375823,53L441.39832191155153,73.51411332232593Q432.84169006347656,78 432.84169006347656,87.66121772182082L432.84169006347656,99" id="export-svg-L_O_Res_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 69.0122299194336 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_O_Res_0" data-points="W3sieCI6NDgwLjUyODA5MjIzMzc1ODIzLCJ5Ijo1M30seyJ4Ijo0MzIuODQxNjkwMDYzNDc2NTYsInkiOjc4fSx7IngiOjQzMi44NDE2OTAwNjM0NzY1NiwieSI6MTAzfV0=" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M566.3636161402652,53L605.4933864624719,73.51411332232593Q614.0500183105469,78 614.0500183105469,87.66121772182082L614.0500183105469,99" id="export-svg-L_O_Plan_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 69.01223754882812 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_O_Plan_0" data-points="W3sieCI6NTY2LjM2MzYxNjE0MDI2NTIsInkiOjUzfSx7IngiOjYxNC4wNTAwMTgzMTA1NDY5LCJ5Ijo3OH0seyJ4Ijo2MTQuMDUwMDE4MzEwNTQ2OSwieSI6MTAzfV0=" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M554.4750213623047,130.88115003172175L95.55727713789064,172.33319133079797Q88.17500305175781,173 88.17500305175781,180.4123278721405L88.17500305175781,194" id="export-svg-L_Plan_Manifest_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 482.69073486328125 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Plan_Manifest_0" data-points="W3sieCI6NTU0LjQ3NTAyMTM2MjMwNDcsInkiOjEzMC44ODExNTAwMzE3MjE3NX0seyJ4Ijo4OC4xNzUwMDMwNTE3NTc4MSwieSI6MTczfSx7IngiOjg4LjE3NTAwMzA1MTc1NzgxLCJ5IjoxOTh9XQ==" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M554.4750213623047,134.4685833862029L306.1041427352862,171.85901645002903Q298.52500915527344,173 298.52500915527344,180.66453581666764L298.52500915527344,194" id="export-svg-L_Plan_Params_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 273.42950439453125 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Plan_Params_0" data-points="W3sieCI6NTU0LjQ3NTAyMTM2MjMwNDcsInkiOjEzNC40Njg1ODMzODYyMDI5fSx7IngiOjI5OC41MjUwMDkxNTUyNzM0NCwieSI6MTczfSx7IngiOjI5OC41MjUwMDkxNTUyNzM0NCwieSI6MTk4fV0=" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M564.2302800228721,148L517.2762006544134,169.20578754729604Q508.87501525878906,173 508.87501525878906,182.21824084019863L508.87501525878906,194" id="export-svg-L_Plan_Wrapper_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 75.8083267211914 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Plan_Wrapper_0" data-points="W3sieCI6NTY0LjIzMDI4MDAyMjg3MjEsInkiOjE0OH0seyJ4Ijo1MDguODc1MDE1MjU4Nzg5MDYsInkiOjE3M30seyJ4Ijo1MDguODc1MDE1MjU4Nzg5MDYsInkiOjE5OH1d" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M663.8697565982217,148L710.8238359666803,169.20578754729604Q719.2250213623047,173 719.2250213623047,182.21824084019863L719.2250213623047,194" id="export-svg-L_Plan_Docs_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 75.80831146240234 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Plan_Docs_0" data-points="W3sieCI6NjYzLjg2OTc1NjU5ODIyMTcsInkiOjE0OH0seyJ4Ijo3MTkuMjI1MDIxMzYyMzA0NywieSI6MTczfSx7IngiOjcxOS4yMjUwMjEzNjIzMDQ3LCJ5IjoxOTh9XQ==" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M673.6250152587891,134.4685833862029L921.9958938858076,171.85901645002903Q929.5750274658203,173 929.5750274658203,180.66453581666764L929.5750274658203,194" id="export-svg-L_Plan_Tests_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 273.4295654296875 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Plan_Tests_0" data-points="W3sieCI6NjczLjYyNTAxNTI1ODc4OTEsInkiOjEzNC40Njg1ODMzODYyMDI5fSx7IngiOjkyOS41NzUwMjc0NjU4MjAzLCJ5IjoxNzN9LHsieCI6OTI5LjU3NTAyNzQ2NTgyMDMsInkiOjE5OH1d" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M673.6250152587891,130.88115003172175L1132.542759483203,172.33319133079797Q1139.925033569336,173 1139.925033569336,180.4123278721405L1139.925033569336,194" id="export-svg-L_Plan_Dockerfile_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 482.6909484863281 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Plan_Dockerfile_0" data-points="W3sieCI6NjczLjYyNTAxNTI1ODc4OTEsInkiOjEzMC44ODExNTAwMzE3MjE3NX0seyJ4IjoxMTM5LjkyNTAzMzU2OTMzNiwieSI6MTczfSx7IngiOjExMzkuOTI1MDMzNTY5MzM2LCJ5IjoxOTh9XQ==" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_O_Res_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_O_Plan_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Plan_Manifest_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Plan_Params_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Plan_Wrapper_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Plan_Docs_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Plan_Tests_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Plan_Dockerfile_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="root" transform="translate(1051.7500305175781, 190)"&gt;&lt;g class="clusters"&gt;&lt;g class="cluster" id="export-svg-Dockerfile" data-id="Dockerfile" data-et="cluster" data-look="neo"&gt;&lt;rect style="" x="8" y="8" width="160.35000610351562" height="360"/&gt;&lt;g class="cluster-label" transform="translate(37.600006103515625, 8)"&gt;&lt;foreignObject width="101.14999389648438" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Dockerfile agent&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M88.17500305175781,90.5L88.17500305175781,128L88.17500305175781,161.5" id="export-svg-L_Kgen_Kval_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Kgen_Kval_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjkwLjV9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjEyOH0seyJ4Ijo4OC4xNzUwMDMwNTE3NTc4MSwieSI6MTY1LjV9XQ==" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M88.17500305175781,210.5L88.17500305175781,248L88.17500305175781,281.5" id="export-svg-L_Kval_Kcor_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Kval_Kcor_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjIxMC41fSx7IngiOjg4LjE3NTAwMzA1MTc1NzgxLCJ5IjoyNDh9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjI4NS41fV0=" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Kgen_Kval_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Kval_Kcor_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="export-svg-flowchart-Kcor-15" data-look="neo" data-id="Kcor" data-node="true" data-et="node" transform="translate(88.17500305175781, 308)"&gt;&lt;rect class="basic label-container" style="" data-id="Kcor" x="-38.94999694824219" y="-22.5" width="77.89999389648438" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-22.949996948242188, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="45.899993896484375" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Correct&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Kgen-17" data-look="neo" data-id="Kgen" data-node="true" data-et="node" transform="translate(88.17500305175781, 68)"&gt;&lt;rect class="basic label-container" style="" data-id="Kgen" x="-45.17500305175781" y="-22.5" width="90.35000610351562" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-29.175003051757812, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="58.350006103515625" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Generate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Kval-16" data-look="neo" data-id="Kval" data-node="true" data-et="node" transform="translate(88.17500305175781, 188)"&gt;&lt;rect class="basic label-container" style="" data-id="Kval" x="-40.775001525878906" y="-22.5" width="81.55000305175781" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-24.775001525878906, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="49.55000305175781" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Validate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="root" transform="translate(841.4000244140625, 190)"&gt;&lt;g class="clusters"&gt;&lt;g class="cluster" id="export-svg-Tests" data-id="Tests" data-et="cluster" data-look="neo"&gt;&lt;rect style="" x="8" y="8" width="160.35000610351562" height="360"/&gt;&lt;g class="cluster-label" transform="translate(52.39167022705078, 8)"&gt;&lt;foreignObject width="71.56666564941406" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Tests agent&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M88.17500305175781,90.5L88.17500305175781,128L88.17500305175781,161.5" id="export-svg-L_Tgen_Tval_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Tgen_Tval_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjkwLjV9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjEyOH0seyJ4Ijo4OC4xNzUwMDMwNTE3NTc4MSwieSI6MTY1LjV9XQ==" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M88.17500305175781,210.5L88.17500305175781,248L88.17500305175781,281.5" id="export-svg-L_Tval_Tcor_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Tval_Tcor_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjIxMC41fSx7IngiOjg4LjE3NTAwMzA1MTc1NzgxLCJ5IjoyNDh9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjI4NS41fV0=" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Tgen_Tval_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Tval_Tcor_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="export-svg-flowchart-Tcor-12" data-look="neo" data-id="Tcor" data-node="true" data-et="node" transform="translate(88.17500305175781, 308)"&gt;&lt;rect class="basic label-container" style="" data-id="Tcor" x="-38.94999694824219" y="-22.5" width="77.89999389648438" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-22.949996948242188, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="45.899993896484375" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Correct&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Tgen-14" data-look="neo" data-id="Tgen" data-node="true" data-et="node" transform="translate(88.17500305175781, 68)"&gt;&lt;rect class="basic label-container" style="" data-id="Tgen" x="-45.17500305175781" y="-22.5" width="90.35000610351562" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-29.175003051757812, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="58.350006103515625" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Generate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Tval-13" data-look="neo" data-id="Tval" data-node="true" data-et="node" transform="translate(88.17500305175781, 188)"&gt;&lt;rect class="basic label-container" style="" data-id="Tval" x="-40.775001525878906" y="-22.5" width="81.55000305175781" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-24.775001525878906, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="49.55000305175781" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Validate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="root" transform="translate(631.0500183105469, 190)"&gt;&lt;g class="clusters"&gt;&lt;g class="cluster" id="export-svg-Docs" data-id="Docs" data-et="cluster" data-look="neo"&gt;&lt;rect style="" x="8" y="8" width="160.35000610351562" height="360"/&gt;&lt;g class="cluster-label" transform="translate(52.775001525878906, 8)"&gt;&lt;foreignObject width="70.80000305175781" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Docs agent&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M88.17500305175781,90.5L88.17500305175781,128L88.17500305175781,161.5" id="export-svg-L_Dgen_Dval_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Dgen_Dval_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjkwLjV9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjEyOH0seyJ4Ijo4OC4xNzUwMDMwNTE3NTc4MSwieSI6MTY1LjV9XQ==" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M88.17500305175781,210.5L88.17500305175781,248L88.17500305175781,281.5" id="export-svg-L_Dval_Dcor_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Dval_Dcor_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjIxMC41fSx7IngiOjg4LjE3NTAwMzA1MTc1NzgxLCJ5IjoyNDh9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjI4NS41fV0=" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Dgen_Dval_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Dval_Dcor_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="export-svg-flowchart-Dcor-9" data-look="neo" data-id="Dcor" data-node="true" data-et="node" transform="translate(88.17500305175781, 308)"&gt;&lt;rect class="basic label-container" style="" data-id="Dcor" x="-38.94999694824219" y="-22.5" width="77.89999389648438" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-22.949996948242188, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="45.899993896484375" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Correct&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Dgen-11" data-look="neo" data-id="Dgen" data-node="true" data-et="node" transform="translate(88.17500305175781, 68)"&gt;&lt;rect class="basic label-container" style="" data-id="Dgen" x="-45.17500305175781" y="-22.5" width="90.35000610351562" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-29.175003051757812, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="58.350006103515625" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Generate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Dval-10" data-look="neo" data-id="Dval" data-node="true" data-et="node" transform="translate(88.17500305175781, 188)"&gt;&lt;rect class="basic label-container" style="" data-id="Dval" x="-40.775001525878906" y="-22.5" width="81.55000305175781" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-24.775001525878906, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="49.55000305175781" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Validate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="root" transform="translate(420.70001220703125, 190)"&gt;&lt;g class="clusters"&gt;&lt;g class="cluster" id="export-svg-Wrapper" data-id="Wrapper" data-et="cluster" data-look="neo"&gt;&lt;rect style="" x="8" y="8" width="160.35000610351562" height="360"/&gt;&lt;g class="cluster-label" transform="translate(42.008338928222656, 8)"&gt;&lt;foreignObject width="92.33332824707031" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Wrapper agent&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M88.17500305175781,90.5L88.17500305175781,128L88.17500305175781,161.5" id="export-svg-L_Wgen_Wval_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Wgen_Wval_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjkwLjV9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjEyOH0seyJ4Ijo4OC4xNzUwMDMwNTE3NTc4MSwieSI6MTY1LjV9XQ==" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M88.17500305175781,210.5L88.17500305175781,248L88.17500305175781,281.5" id="export-svg-L_Wval_Wcor_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Wval_Wcor_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjIxMC41fSx7IngiOjg4LjE3NTAwMzA1MTc1NzgxLCJ5IjoyNDh9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjI4NS41fV0=" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Wgen_Wval_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Wval_Wcor_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="export-svg-flowchart-Wcor-6" data-look="neo" data-id="Wcor" data-node="true" data-et="node" transform="translate(88.17500305175781, 308)"&gt;&lt;rect class="basic label-container" style="" data-id="Wcor" x="-38.94999694824219" y="-22.5" width="77.89999389648438" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-22.949996948242188, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="45.899993896484375" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Correct&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Wgen-8" data-look="neo" data-id="Wgen" data-node="true" data-et="node" transform="translate(88.17500305175781, 68)"&gt;&lt;rect class="basic label-container" style="" data-id="Wgen" x="-45.17500305175781" y="-22.5" width="90.35000610351562" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-29.175003051757812, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="58.350006103515625" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Generate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Wval-7" data-look="neo" data-id="Wval" data-node="true" data-et="node" transform="translate(88.17500305175781, 188)"&gt;&lt;rect class="basic label-container" style="" data-id="Wval" x="-40.775001525878906" y="-22.5" width="81.55000305175781" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-24.775001525878906, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="49.55000305175781" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Validate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="root" transform="translate(210.35000610351562, 190)"&gt;&lt;g class="clusters"&gt;&lt;g class="cluster" id="export-svg-Params" data-id="Params" data-et="cluster" data-look="neo"&gt;&lt;rect style="" x="8" y="8" width="160.35000610351562" height="360"/&gt;&lt;g class="cluster-label" transform="translate(44.60833740234375, 8)"&gt;&lt;foreignObject width="87.13333129882812" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Params agent&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M88.17500305175781,90.5L88.17500305175781,128L88.17500305175781,161.5" id="export-svg-L_Pagen_Paval_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Pagen_Paval_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjkwLjV9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjEyOH0seyJ4Ijo4OC4xNzUwMDMwNTE3NTc4MSwieSI6MTY1LjV9XQ==" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M88.17500305175781,210.5L88.17500305175781,248L88.17500305175781,281.5" id="export-svg-L_Paval_Pacor_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Paval_Pacor_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjIxMC41fSx7IngiOjg4LjE3NTAwMzA1MTc1NzgxLCJ5IjoyNDh9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjI4NS41fV0=" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Pagen_Paval_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Paval_Pacor_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="export-svg-flowchart-Pacor-3" data-look="neo" data-id="Pacor" data-node="true" data-et="node" transform="translate(88.17500305175781, 308)"&gt;&lt;rect class="basic label-container" style="" data-id="Pacor" x="-38.94999694824219" y="-22.5" width="77.89999389648438" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-22.949996948242188, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="45.899993896484375" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Correct&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Pagen-5" data-look="neo" data-id="Pagen" data-node="true" data-et="node" transform="translate(88.17500305175781, 68)"&gt;&lt;rect class="basic label-container" style="" data-id="Pagen" x="-45.17500305175781" y="-22.5" width="90.35000610351562" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-29.175003051757812, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="58.350006103515625" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Generate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Paval-4" data-look="neo" data-id="Paval" data-node="true" data-et="node" transform="translate(88.17500305175781, 188)"&gt;&lt;rect class="basic label-container" style="" data-id="Paval" x="-40.775001525878906" y="-22.5" width="81.55000305175781" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-24.775001525878906, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="49.55000305175781" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Validate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="root" transform="translate(0, 190)"&gt;&lt;g class="clusters"&gt;&lt;g class="cluster" id="export-svg-Manifest" data-id="Manifest" data-et="cluster" data-look="neo"&gt;&lt;rect style="" x="8" y="8" width="160.35000610351562" height="360"/&gt;&lt;g class="cluster-label" transform="translate(42.275001525878906, 8)"&gt;&lt;foreignObject width="91.80000305175781" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Manifest agent&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M88.17500305175781,90.5L88.17500305175781,128L88.17500305175781,161.5" id="export-svg-L_Mgen_Mval_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Mgen_Mval_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjkwLjV9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjEyOH0seyJ4Ijo4OC4xNzUwMDMwNTE3NTc4MSwieSI6MTY1LjV9XQ==" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;path d="M88.17500305175781,210.5L88.17500305175781,248L88.17500305175781,281.5" id="export-svg-L_Mval_Mcor_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style="stroke-dasharray: 0 0 67 4; stroke-dashoffset: 0;;" data-edge="true" data-et="edge" data-id="L_Mval_Mcor_0" data-points="W3sieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjIxMC41fSx7IngiOjg4LjE3NTAwMzA1MTc1NzgxLCJ5IjoyNDh9LHsieCI6ODguMTc1MDAzMDUxNzU3ODEsInkiOjI4NS41fV0=" data-look="neo" marker-end="url(#export-svg_flowchart-v2-pointEnd-margin)"/&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Mgen_Mval_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_Mval_Mcor_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="export-svg-flowchart-Mcor-0" data-look="neo" data-id="Mcor" data-node="true" data-et="node" transform="translate(88.17500305175781, 308)"&gt;&lt;rect class="basic label-container" style="" data-id="Mcor" x="-38.94999694824219" y="-22.5" width="77.89999389648438" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-22.949996948242188, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="45.899993896484375" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Correct&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Mgen-2" data-look="neo" data-id="Mgen" data-node="true" data-et="node" transform="translate(88.17500305175781, 68)"&gt;&lt;rect class="basic label-container" style="" data-id="Mgen" x="-45.17500305175781" y="-22.5" width="90.35000610351562" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-29.175003051757812, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="58.350006103515625" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Generate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Mval-1" data-look="neo" data-id="Mval" data-node="true" data-et="node" transform="translate(88.17500305175781, 188)"&gt;&lt;rect class="basic label-container" style="" data-id="Mval" x="-40.775001525878906" y="-22.5" width="81.55000305175781" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-24.775001525878906, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="49.55000305175781" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Validate&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-O-18" data-look="neo" data-id="O" data-node="true" data-et="node" transform="translate(523.4458541870117, 30.5)"&gt;&lt;rect class="basic label-container" style="" data-id="O" x="-54.89167022705078" y="-22.5" width="109.78334045410156" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-38.89167022705078, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="77.78334045410156" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Orchestrator&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Res-19" data-look="neo" data-id="Res" data-node="true" data-et="node" transform="translate(432.84169006347656, 125.5)"&gt;&lt;rect class="basic label-container" style="" data-id="Res" x="-71.63333129882812" y="-22.5" width="143.26666259765625" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-55.633331298828125, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="111.26666259765625" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Researcher agent&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="export-svg-flowchart-Plan-20" data-look="neo" data-id="Plan" data-node="true" data-et="node" transform="translate(614.0500183105469, 125.5)"&gt;&lt;rect class="basic label-container" style="" data-id="Plan" x="-59.57499694824219" y="-22.5" width="119.14999389648438" height="45" stroke="url(#gradient)"/&gt;&lt;g class="label" style="" transform="translate(-43.57499694824219, -10.5)"&gt;&lt;rect/&gt;&lt;foreignObject width="87.14999389648438" height="21"&gt;&lt;div style="display: table-cell; white-space: normal; overflow-wrap: break-word; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Planner agent&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id="export-svg-drop-shadow" height="130%" width="130%"&gt;&lt;feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"/&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id="export-svg-drop-shadow-small" height="150%" width="150%"&gt;&lt;feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"/&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;linearGradient id="export-svg-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"&gt;&lt;stop offset="0%" stop-color="hsl(40.5882352941, 60%, 83.3333333333%)" stop-opacity="1"/&gt;&lt;stop offset="100%" stop-color="hsl(-79.4117647059, 60%, 83.3333333333%)" stop-opacity="1"/&gt;&lt;/linearGradient&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;h2&gt;Starting with the scaffolding, not the model&lt;/h2&gt;
&lt;p&gt;GenePattern was old and had grown organically enough over two decades. It never had a formal definition for what a manifest file was supposed to contain, let alone the rest of its artifacts. This was a problem that needed to be solved before I could even think about agents: there was no ground truth to check anything against.&lt;/p&gt;
&lt;p&gt;So the first thing I built for an AI-driven project was not AI. It was linter scripts, effectively defining a de facto standard for what a valid GenePattern manifest, and the other artifacts, look like. &lt;/p&gt;
&lt;blockquote class="float-right w-[300px] ml-8 mb-4 pl-5 border-l-[3px] border-gray-400 dark:border-gray-600
         font-serif text-xl leading-snug
         max-[700px]:float-none max-[700px]:w-auto max-[700px]:ml-0" aria-hidden="true"&gt;
  The harness around a model matters at least as much as the model itself. Without it you don't have an agent, you have a very confident guess.
&lt;/blockquote&gt;

&lt;p&gt;Manifests are Java properties files. Every required key has to be present, every value has to be of a type the platform expects and a literal &lt;code&gt;=&lt;/code&gt; inside a value has to be escaped or the parser reads it as the start of a new key. None of this is exotic. It's the kind of thing a human engineer internalizes after their third or fourth manifest and then never thinks about again. It is also exactly the kind of detail an LLM will cheerfully get wrong, because nothing about "write a manifest" tells it that an unescaped &lt;code&gt;=&lt;/code&gt; is fatal. &lt;/p&gt;
&lt;p&gt;The linters caught missing keys, wrong value types, unescaped &lt;code&gt;=&lt;/code&gt; characters, dependencies missing from the Dockerfile and property names that quietly drifted between artifacts that were supposed to agree with each other. Those scripts would go on to become the backbone of the chain-of-verification pattern, but at the time they were just me trying to codify rules that had only ever existed as institutional knowledge.&lt;/p&gt;
&lt;p&gt;Is it strange to start an agentic AI project by writing rigid, deterministic scripts that have nothing to do with the model? I don't think so. Every project I've built has reinforced the same lesson: the harness around a model matters at least as much as the model itself. Without it you don't have an agent, you have a very confident guess.&lt;/p&gt;
&lt;h2&gt;Wiring the linters to the AI&lt;/h2&gt;
&lt;p&gt;With the linters working on their own, the next job was hooking them up to something that could actually write the artifacts they were checking. &lt;/p&gt;
&lt;p&gt;For the agent framework I picked &lt;a href="https://pydantic.dev/docs/ai/overview/"&gt;Pydantic AI&lt;/a&gt; because it's fast, lightweight and good at structured output. This mattered because nearly everything in this system, from planning a module to writing its manifest, depended on getting the model to hand back data in a shape you can work with rather than a paragraph you have to regex apart.&lt;/p&gt;
&lt;p&gt;Once that plumbing was in place, the real work started: iterating on prompts, which is a deceptively glamorous phrase for staring at outputs and trying to figure out why an agent decided a parameter should be an integer when it should have been a float. More than once, an artifact would sail through its own generation and then fail the linter for something that looked cosmetic, such as a manifest key spelled slightly differently than the wrapper script expected, and the chain-of-verification loop would kick the run back to the step that actually introduced the problem. &lt;/p&gt;
&lt;p&gt;That's the pattern doing its job: catching the kind of small, specific mistake that wouldn't announce itself until a researcher tried to actually run the module and it silently used the wrong default.&lt;/p&gt;
&lt;h2&gt;Evals before anything else&lt;/h2&gt;
&lt;p&gt;My last project, GenePattern Copilot, taught me a lesson the hard way: if you don't build a formal evaluation suite from the beginning, you end up debugging by vibes, and vibes do not scale. I started this project determined not to repeat that mistake.&lt;/p&gt;
&lt;p&gt;Building a good eval suite from scratch is often a project of its own, but GenePattern had one advantage: it was a mature platform with a large corpus of known-good modules already sitting in production. That corpus became my known-good outputs, and I worked backwards from there, generating synthetic inputs that should produce something close to those known-good modules. Early runs of the toolkit added to that suite too, mostly by failing, with each bad output becoming a documented failure state I could check future runs against.&lt;/p&gt;
&lt;h2&gt;Where the breakages happened&lt;/h2&gt;
&lt;p&gt;A single input-and-output eval may be enough for a system with one model doing one job. It isn't enough for a pipeline with eight subagents, each with its own inputs, outputs and failure modes. I needed to evaluate each one independently, or I'd have no way of knowing which agent was actually responsible when something broke downstream. That's where nearly all of my early failures lived, and this project is what taught me that in a multi-agent system, observability isn't optional.&lt;/p&gt;
&lt;p&gt;Once one agent's output becomes another agent's input, a small ambiguity introduced three steps upstream can surface as a completely unrelated-looking failure two agents later. Trying to debug that by reading logs after the fact felt like reconstructing a car crash from the skid marks. I needed to watch it happen with better clarity.&lt;/p&gt;
&lt;p&gt;I picked &lt;a href="https://github.com/pydantic/logfire"&gt;Logfire&lt;/a&gt; for this, mostly because it's built by the same team behind Pydantic AI and the integration took 10 minutes. This let me trace a single request from the initial prompt through the orchestrator and out through each subagent in order. More importantly, it also let me see exactly where the context handed from one agent to the next started to drift from what the next agent needed. Once I had that visibility, progress on stability went from slow and confusing to fast and specific.&lt;/p&gt;
&lt;h2&gt;Learning to survive a crash&lt;/h2&gt;
&lt;p&gt;A full run of the toolkit was not fast. Between the researcher agent pulling context from the web, the linter scripts running their checks, a Docker image building as part of the containerization tests and the LLM calls themselves, a typical run took twenty to thirty minutes. And in the early days, that half hour was not guaranteed to finish.&lt;/p&gt;
&lt;blockquote class="float-right w-[300px] ml-8 mb-4 pl-5 border-l-[3px] border-gray-400 dark:border-gray-600
         font-serif text-xl leading-snug
         max-[700px]:float-none max-[700px]:w-auto max-[700px]:ml-0" aria-hidden="true"&gt;
  What I'd actually built, without ever calling it that at the time, was a crude, hand-rolled version of durable execution &amp;hellip; a crash costs you the time since your last checkpoint instead of the entire run.
&lt;/blockquote&gt;

&lt;p&gt;Inference calls hung. Web requests from the researcher agent hung. Docker builds ran out of disk space partway through and died. Agents hit their maximum number of retries without resolving whatever had gone wrong. None of this was exotic. It was just what happens when you chain together enough steps that each have their own independent failure modes.&lt;/p&gt;
&lt;p&gt;The fix started small. I wanted a way to test one step of the pipeline in isolation without rerunning the whole thing from scratch every time, so I started to serialize the current state of the pipeline to disk as a status.json file. This also became the artifact each agent handed off to the next. From there it was easy to implement a flag letting a run resume from an existing status.json. A handful of additional flags let me target specific steps or ranges of steps directly. This made both testing and recovery faster.&lt;/p&gt;
&lt;p&gt;What I'd actually built, without ever calling it that at the time, was a crude, hand-rolled version of durable execution: a workflow whose state survives the process running it, so that a crash costs you the time since your last checkpoint instead of the entire run. That wasn't my initial goal. My goal was to get my afternoon back after watching a Docker build die partway through for the third time. But once it existed, I couldn't unsee how much of the pipeline's actual reliability was coming from that one property rather than from anything about the agents themselves.&lt;/p&gt;
&lt;h2&gt;The planner agent&lt;/h2&gt;
&lt;p&gt;The planner wasn't part of my original design. It got added later because of a specific, repeated failure: the agents kept producing artifacts that were each individually correct, but which didn't agree with each other. The manifest agent might settle on one set of parameter names while the wrapper script agent settled on a slightly different set. Each would pass its own linter checks in isolation, then fail the moment you tried to assemble them into one working whole.&lt;/p&gt;
&lt;p&gt;The planner's job is to make that decision once, upstream of the six artifact agents, instead of letting the six agents make six independent guesses. It looks at the researcher agent's report and figures out the right general approach: an R script gets handled differently than a Python library, which gets handled differently than a raw shell script or a compiled C binary, etc. It decides which parameters are worth exposing, and settles on the actual names and descriptions those parameters will have in GenePattern's interface. All this becomes shared context for every downstream agent.&lt;/p&gt;
&lt;h2&gt;Handing it to the interns&lt;/h2&gt;
&lt;p&gt;Once the pipeline was running reliably for me, it was time to see if it worked for anyone else. So we handed it to our interns and asked them to use it for the modules already on their plate.&lt;/p&gt;
&lt;p&gt;It broke almost immediately. Runs failed constantly at the planning stage, or hit their maximum iteration count in the first two artifact stages before anything useful came out.&lt;/p&gt;
&lt;p&gt;Part of this was a decision made above my pay grade, and a reasonable one. We'd just had an API key leak that racked up a sizable bill before anyone noticed, and the lab had no interest in handing a new set of keys to a room full of interns. Instead, we set them up with a local Qwen model we'd tested ourselves and found passable, albeit not nearly as strong as Claude.&lt;/p&gt;
&lt;p&gt;This worked for me. It failed for most of them. I still don't have a clean explanation for the gap between my machine and theirs, but the failure I could actually see and fix was structural: Qwen produced malformed, structurally hallucinated JSON (e.g. invented fields, wrong nesting, values in the wrong shape entirely) far more often than Claude did.&lt;/p&gt;
&lt;p&gt;I spent some real effort trying to close that gap by fine-tuning Qwen at the San Diego Supercomputer Center, and in the end achieved something like a 50 percent reduction in the structural hallucination rate. This was good progress, but it wasn't enough.&lt;/p&gt;
&lt;p&gt;So I made the call: I stopped trying to optimize the model further, and instead threw together a lightweight hosted frontend on AWS, backed by Claude via Bedrock. After that, the reliability problem which had eaten weeks of intern time mostly stopped being a problem.&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;Once the interns had their turn and the production hardening was done, we rolled the toolkit out to the rest of the GenePattern team. As a result, module production went up roughly 500 percent. And along the way I learned a few lessons.&lt;/p&gt;
&lt;blockquote class="float-right w-[300px] ml-8 mb-4 pl-5 border-l-[3px] border-gray-400 dark:border-gray-600
         font-serif text-xl leading-snug
         max-[700px]:float-none max-[700px]:w-auto max-[700px]:ml-0" aria-hidden="true"&gt;
  Sometimes the scaffolding's real value is proving, with evidence, exactly where a model's ceiling sits — turning the decision to replace it into a documented engineering call instead of a shrug.
&lt;/blockquote&gt;

&lt;p&gt;The scaffolding around a model—the linters, the schemas, the harness that tells it when it's wrong—deserves to be built before the model touches anything, not bolted on once things are already unstable.&lt;/p&gt;
&lt;p&gt;A strong planner sitting upstream of your artifact-generating agents will save you from an entire category of bugs that look like model-quality problems, but which are actually coordination problems. Six correct answers that don't agree with each other just aren't that useful. &lt;/p&gt;
&lt;p&gt;Observability isn't optional in anything where one agent talks to another. That's where a lot of non-obvious bugs can hide, rather than in any single agent's output. Before implementing observability, I spent more hours confused than I want to admit.&lt;/p&gt;
&lt;p&gt;Don't overlook durability. Any workflow that runs long enough to be worth building is going to run long enough to fail partway through, and if you haven't decided in advance what happens when it does, you'll end up deciding it anyway, at 5pm, under worse conditions than if you'd just planned for it.&lt;/p&gt;
&lt;p&gt;Sometimes the scaffolding's real value is proving, with evidence, exactly where a model's ceiling sits, turning the decision to replace it into a documented engineering call instead of a shrug.&lt;/p&gt;
&lt;p&gt;None of these lessons are especially profound on their own. But together they compound: Solving the coordination problem with a planner made the durability problem more visible. Better observability made the coordination problem easier to fix. None of it works if the scaffolding underneath is an afterthought. &lt;/p&gt;
&lt;p&gt;That's not a spatialGE story, or even a bioinformatics one. It's just what happens when you build something agentic that has to survive contact with real work.&lt;/p&gt;</content><category term="genepattern"/><category term="agentic-ai"/><category term="open-source"/><category term="pydantic-ai"/></entry></feed>