<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Younes Ben Tlili</title>
    <description>The latest articles on DEV Community by Younes Ben Tlili (@younes_bentlili_9480340f).</description>
    <link>https://dev.to/younes_bentlili_9480340f</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3745169%2Fde9798d8-300b-4dd0-858d-d04b768ee532.jpeg</url>
      <title>DEV Community: Younes Ben Tlili</title>
      <link>https://dev.to/younes_bentlili_9480340f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/younes_bentlili_9480340f"/>
    <language>en</language>
    <item>
      <title>Don't Start With RAG: Lessons From Building an Automotive AI Pipeline</title>
      <dc:creator>Younes Ben Tlili</dc:creator>
      <pubDate>Sun, 16 Aug 2026 18:03:12 +0000</pubDate>
      <link>https://dev.to/younes_bentlili_9480340f/dont-start-with-rag-lessons-from-building-an-automotive-ai-pipeline-2igc</link>
      <guid>https://dev.to/younes_bentlili_9480340f/dont-start-with-rag-lessons-from-building-an-automotive-ai-pipeline-2igc</guid>
      <description>&lt;p&gt;When building an AI product, it's tempting to start with the fashionable pieces.&lt;/p&gt;

&lt;p&gt;Vector database.&lt;/p&gt;

&lt;p&gt;RAG.&lt;/p&gt;

&lt;p&gt;Agents.&lt;/p&gt;

&lt;p&gt;Multimodal models.&lt;/p&gt;

&lt;p&gt;Then connect everything to an LLM and hope the final prompt makes sense of it.&lt;/p&gt;

&lt;p&gt;While building the automotive AI pipeline behind Inspecly, we ended up taking almost the opposite approach.&lt;/p&gt;

&lt;p&gt;The first question wasn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which LLM should we use?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What information do we actually have, how reliable is it, and which system should process it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That distinction changed the architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The input is messy by default&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A driver rarely describes a vehicle problem like a mechanic.&lt;/p&gt;

&lt;p&gt;They might say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"My car makes a strange noise when I start it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But a request can also contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a written description,&lt;/li&gt;
&lt;li&gt;a voice message,&lt;/li&gt;
&lt;li&gt;vehicle photos,&lt;/li&gt;
&lt;li&gt;an OBD scan,&lt;/li&gt;
&lt;li&gt;vehicle metadata,&lt;/li&gt;
&lt;li&gt;or only some of these.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These inputs do not have the same reliability.&lt;/p&gt;

&lt;p&gt;An OBD code is structured information.&lt;/p&gt;

&lt;p&gt;A photo is visual evidence.&lt;/p&gt;

&lt;p&gt;A voice message represents what the driver observed.&lt;/p&gt;

&lt;p&gt;Vehicle metadata may require exact lookup.&lt;/p&gt;

&lt;p&gt;Treating all of them as equivalent pieces of text would be a mistake.&lt;/p&gt;

&lt;p&gt;So before asking an LLM to reason about the problem, we normalize the available evidence.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"vehicle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"make"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"vin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"optional"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The engine loses power when accelerating."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"voice_transcription"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"obd_codes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"images"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This object doesn't contain a diagnosis.&lt;/p&gt;

&lt;p&gt;It describes what we actually know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why not send everything to one multimodal LLM?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You absolutely could.&lt;/p&gt;

&lt;p&gt;Description + images + OBD + vehicle information → one model → final answer.&lt;/p&gt;

&lt;p&gt;It is very attractive for a prototype.&lt;/p&gt;

&lt;p&gt;It's also difficult to control.&lt;/p&gt;

&lt;p&gt;Consider these inputs:&lt;/p&gt;

&lt;p&gt;OBD code&lt;br&gt;
→ deterministic lookup&lt;/p&gt;

&lt;p&gt;Photo&lt;br&gt;
→ visual analysis&lt;/p&gt;

&lt;p&gt;Voice&lt;br&gt;
→ transcription&lt;/p&gt;

&lt;p&gt;Vehicle information&lt;br&gt;
→ exact lookup/API&lt;/p&gt;

&lt;p&gt;Unknown technical information&lt;br&gt;
→ tool-based retrieval&lt;/p&gt;

&lt;p&gt;Safety constraint&lt;br&gt;
→ explicit business rule&lt;/p&gt;

&lt;p&gt;These are fundamentally different operations.&lt;/p&gt;

&lt;p&gt;Putting everything into one giant prompt hides those differences.&lt;/p&gt;

&lt;p&gt;It also becomes much harder to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where did this conclusion come from?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a serious problem once the system moves beyond a demo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured data comes first&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One design decision became particularly important for us:&lt;/p&gt;

&lt;p&gt;If reliable structured information already exists, use it directly.&lt;/p&gt;

&lt;p&gt;For example, when an OBD diagnostic trouble code is available, we first query our curated OBD database.&lt;/p&gt;

&lt;p&gt;A record can contain information such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"explanation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"possible_causes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"possible_actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"validation_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reviewed"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why retrieve semantically similar paragraphs from documents when the system can perform an exact lookup against validated fields?&lt;/p&gt;

&lt;p&gt;This is an important distinction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured data and RAG solve different problems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Structured data is excellent for:&lt;/p&gt;

&lt;p&gt;exact identifiers,&lt;br&gt;
validated fields,&lt;br&gt;
controlled records,&lt;br&gt;
deterministic queries.&lt;/p&gt;

&lt;p&gt;RAG becomes useful when knowledge primarily lives inside documents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if the structured knowledge is missing?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's where agents become useful.&lt;/p&gt;

&lt;p&gt;Our internal database cannot contain every code, every manufacturer-specific interpretation and every vehicle configuration.&lt;/p&gt;

&lt;p&gt;When structured knowledge is missing, a tool-using agent can search for additional information.&lt;/p&gt;

&lt;p&gt;But there is an important rule:&lt;/p&gt;

&lt;p&gt;Retrieved information should not silently become equivalent to validated information.&lt;/p&gt;

&lt;p&gt;Instead, preserve provenance.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool_agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"validation_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unverified"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final system should know whether a piece of information came from:&lt;/p&gt;

&lt;p&gt;a reviewed internal database,&lt;br&gt;
an external technical source,&lt;br&gt;
a tool-using agent,&lt;br&gt;
image analysis,&lt;br&gt;
or the driver themselves.&lt;/p&gt;

&lt;p&gt;The model doesn't only need context.&lt;/p&gt;

&lt;p&gt;It needs context with provenance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Images are evidence, not diagnosis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vision models are another useful component.&lt;/p&gt;

&lt;p&gt;A photo might reveal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a dashboard warning light,&lt;/li&gt;
&lt;li&gt;visible body damage,&lt;/li&gt;
&lt;li&gt;tyre wear,&lt;/li&gt;
&lt;li&gt;a fluid trace,&lt;/li&gt;
&lt;li&gt;a damaged component.
But a photograph almost never tells the whole story.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The output should therefore look closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"observation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Possible fluid trace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"medium"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limitations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The source is not visible"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"requires_physical_inspection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your vehicle has an oil leak.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That difference matters.&lt;/p&gt;

&lt;p&gt;AI systems often sound more certain than the evidence actually allows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Voice is just another source of context&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Voice is valuable because describing a mechanical problem through a form can be difficult.&lt;/p&gt;

&lt;p&gt;The message gets transcribed, then becomes another input to the evidence layer.&lt;/p&gt;

&lt;p&gt;But again:&lt;/p&gt;

&lt;h2&gt;
  
  
  "The engine is overheating."
&lt;/h2&gt;

&lt;p&gt;is not necessarily a confirmed technical fact.&lt;/p&gt;

&lt;p&gt;It's something reported by the driver.&lt;/p&gt;

&lt;p&gt;That distinction should survive the entire pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The next step: a structured evidence layer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Today, multiple processing paths can eventually contribute information to the generation context.&lt;/p&gt;

&lt;p&gt;As the number of sources increases, simple concatenation becomes harder to control.&lt;/p&gt;

&lt;p&gt;You eventually need something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reported_symptoms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"obd_findings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"visual_findings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retrieved_information"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"missing_information"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"conflicts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"safety_flags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final LLM can then generate from this normalized evidence instead of receiving an unstructured wall of text.&lt;/p&gt;

&lt;p&gt;This makes several things easier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provenance,&lt;/li&gt;
&lt;li&gt;conflict detection,&lt;/li&gt;
&lt;li&gt;confidence handling,&lt;/li&gt;
&lt;li&gt;evaluation,&lt;/li&gt;
&lt;li&gt;safety rules,&lt;/li&gt;
&lt;li&gt;debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also makes the architecture much easier to evolve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So where is RAG?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We aren't starting with a large automotive RAG pipeline.&lt;/p&gt;

&lt;p&gt;And that's deliberate.&lt;/p&gt;

&lt;p&gt;Adding PDFs to a vector database isn't the difficult part.&lt;/p&gt;

&lt;p&gt;The difficult part is knowing whether a retrieved procedure applies to the correct:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manufacturer,&lt;/li&gt;
&lt;li&gt;model,&lt;/li&gt;
&lt;li&gt;year,&lt;/li&gt;
&lt;li&gt;engine,&lt;/li&gt;
&lt;li&gt;transmission,&lt;/li&gt;
&lt;li&gt;vehicle version,&lt;/li&gt;
&lt;li&gt;document revision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A perfectly retrieved technical procedure for the wrong engine generation can still be completely wrong for the vehicle in front of you.&lt;/p&gt;

&lt;p&gt;So our current priority is:&lt;/p&gt;

&lt;p&gt;Validated structured data&lt;br&gt;
        ↓&lt;br&gt;
Explicit rules / APIs&lt;br&gt;
        ↓&lt;br&gt;
Tool-based retrieval when needed&lt;br&gt;
        ↓&lt;br&gt;
LLM generation&lt;/p&gt;

&lt;p&gt;RAG becomes much more valuable when we have a controlled corpus of manufacturer manuals and validated technical documentation.&lt;/p&gt;

&lt;p&gt;Eventually, the source router could look roughly like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validated structured record&lt;/li&gt;
&lt;li&gt;Explicit safety/business rule&lt;/li&gt;
&lt;li&gt;Vehicle-specific API&lt;/li&gt;
&lt;li&gt;Retrieved validated documentation&lt;/li&gt;
&lt;li&gt;Tool-agent fallback&lt;/li&gt;
&lt;li&gt;Model hypothesis&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This isn't a universal hierarchy.&lt;/p&gt;

&lt;p&gt;The broader point is more important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A generated or retrieved paragraph should not silently override a reviewed fact.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Two audiences, same evidence&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another interesting problem is that the same evidence needs different outputs.&lt;/p&gt;

&lt;p&gt;A driver needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple language,&lt;/li&gt;
&lt;li&gt;uncertainty explained clearly,&lt;/li&gt;
&lt;li&gt;the next useful action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A garage needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vehicle context,&lt;/li&gt;
&lt;li&gt;reported symptoms,&lt;/li&gt;
&lt;li&gt;OBD information,&lt;/li&gt;
&lt;li&gt;technical observations,&lt;/li&gt;
&lt;li&gt;missing information,&lt;/li&gt;
&lt;li&gt;potential directions to investigate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We don't need two independent AI analyses.&lt;/p&gt;

&lt;p&gt;We need two representations of the same evidence.&lt;/p&gt;

&lt;p&gt;That distinction has become an important part of the product architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I'm learning from building this&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The quality of an AI response doesn't start with the final prompt.&lt;/p&gt;

&lt;p&gt;It starts much earlier:&lt;/p&gt;

&lt;p&gt;Input collection&lt;br&gt;
      ↓&lt;br&gt;
Normalization&lt;br&gt;
      ↓&lt;br&gt;
Source routing&lt;br&gt;
      ↓&lt;br&gt;
Evidence + provenance&lt;br&gt;
      ↓&lt;br&gt;
Conflict / uncertainty handling&lt;br&gt;
      ↓&lt;br&gt;
Generation&lt;/p&gt;

&lt;p&gt;A better model can improve generation.&lt;/p&gt;

&lt;p&gt;It cannot fix an architecture where every source is mixed into one untraceable context.&lt;/p&gt;

&lt;p&gt;My current takeaway is simple:&lt;/p&gt;

&lt;p&gt;Use deterministic systems where knowledge is deterministic.&lt;/p&gt;

&lt;p&gt;Use agents where flexibility is useful.&lt;/p&gt;

&lt;p&gt;Use RAG when document retrieval is actually the problem.&lt;/p&gt;

&lt;p&gt;And preserve uncertainty instead of asking the LLM to hide it behind a confident answer.&lt;/p&gt;

&lt;p&gt;I wrote a more detailed version of this architecture — including the current pipeline, future evidence layer and planned RAG architecture — here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Original deep dive:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://younes.hashnode.dev/inside-inspecly-s-automotive-ai-pipeline-from-driver-symptoms-to-actionable-garage-requests" rel="noopener noreferrer"&gt;https://younes.hashnode.dev/inside-inspecly-s-automotive-ai-pipeline-from-driver-symptoms-to-actionable-garage-requests&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm also building AI DevList, where I curate useful resources about agents, LLM engineering, MCP, RAG, evals and production AI — with a short explanation of why each resource matters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aidevlist.io/" rel="noopener noreferrer"&gt;https://aidevlist.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm curious how other teams handle this:&lt;/p&gt;

&lt;p&gt;Do you start from the LLM and build outward, or from the evidence and build inward?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Built AI DevList Because Finding AI Content Is No Longer the Problem</title>
      <dc:creator>Younes Ben Tlili</dc:creator>
      <pubDate>Sun, 16 Aug 2026 17:47:49 +0000</pubDate>
      <link>https://dev.to/younes_bentlili_9480340f/i-built-ai-devlist-because-finding-ai-content-is-no-longer-the-problem-502b</link>
      <guid>https://dev.to/younes_bentlili_9480340f/i-built-ai-devlist-because-finding-ai-content-is-no-longer-the-problem-502b</guid>
      <description>&lt;p&gt;There is no shortage of AI content.&lt;/p&gt;

&lt;p&gt;Actually, we probably have the opposite problem.&lt;/p&gt;

&lt;p&gt;Every day there is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a new agent framework,&lt;/li&gt;
&lt;li&gt;a new MCP server,&lt;/li&gt;
&lt;li&gt;another RAG approach,&lt;/li&gt;
&lt;li&gt;another benchmark,&lt;/li&gt;
&lt;li&gt;another model release,&lt;/li&gt;
&lt;li&gt;another “production-ready” architecture,&lt;/li&gt;
&lt;li&gt;another GitHub repository trendi
ng for 48 hours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an engineer, I don't struggle to find AI content anymore.&lt;/p&gt;

&lt;p&gt;I struggle to decide:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is actually worth my time?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's why I built &lt;strong&gt;AI DevList&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://aidevlist.io/" rel="noopener noreferrer"&gt;https://aidevlist.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not another AI news newsletter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I don't want AI DevList to become a summary of everything that happened in AI during the week.&lt;/p&gt;

&lt;p&gt;There are already excellent newsletters doing that.&lt;/p&gt;

&lt;p&gt;The focus is much narrower:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;resources that are useful for people actually building AI systems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The main topics are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agentic AI&lt;/li&gt;
&lt;li&gt;LLM Engineering&lt;/li&gt;
&lt;li&gt;MCP&lt;/li&gt;
&lt;li&gt;RAG&lt;/li&gt;
&lt;li&gt;Evals&lt;/li&gt;
&lt;li&gt;Tools &amp;amp; Frameworks&lt;/li&gt;
&lt;li&gt;Research&lt;/li&gt;
&lt;li&gt;Open Source&lt;/li&gt;
&lt;li&gt;Production AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to publish 50 links.&lt;/p&gt;

&lt;p&gt;If an issue contains 10–15 genuinely useful resources, that's enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Curation alone isn't enough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I first announced the project, someone gave me a very good piece of feedback:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Curating content is useful, but the real value comes from adding context.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I agree.&lt;/p&gt;

&lt;p&gt;So every resource selected for AI DevList now has two parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. What is it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A short, neutral description of the resource.&lt;/p&gt;

&lt;p&gt;No marketing rewrite.&lt;/p&gt;

&lt;p&gt;No exaggerated claims.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Why we picked it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the important part.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why we picked it&lt;/strong&gt;&lt;br&gt;
Stateless MCP isn't just a protocol detail. It changes how remote MCP servers can be deployed and scaled, removing part of the session-management complexity that infrastructure teams previously had to handle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why we picked it&lt;/strong&gt;&lt;br&gt;
Adding a SKILL.md file doesn't guarantee that an agent will reliably discover or follow it. This benchmark is interesting because it evaluates the complete model + harness + skill combination rather than treating skills as static prompt files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's much closer to what I personally want when someone recommends a technical article to me.&lt;/p&gt;

&lt;p&gt;Don't only tell me what it says.&lt;/p&gt;

&lt;p&gt;Tell me why I should care as an engineer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My current curation workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The workflow is deliberately simple.&lt;/p&gt;

&lt;p&gt;Discover resources&lt;br&gt;
       ↓&lt;br&gt;
Filter obvious noise&lt;br&gt;
       ↓&lt;br&gt;
Read / inspect the source&lt;br&gt;
       ↓&lt;br&gt;
Check technical relevance&lt;br&gt;
       ↓&lt;br&gt;
Categorize&lt;br&gt;
       ↓&lt;br&gt;
Write a short description&lt;br&gt;
       ↓&lt;br&gt;
Add "Why we picked it"&lt;br&gt;
       ↓&lt;br&gt;
Human review&lt;br&gt;
       ↓&lt;br&gt;
Publish&lt;/p&gt;

&lt;p&gt;AI helps with discovery and preparation.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; decide what gets published.&lt;/p&gt;

&lt;p&gt;That's an important distinction for me.&lt;/p&gt;

&lt;p&gt;I don't want to build an automatically generated newsletter that summarizes whatever happens to rank highly that week.&lt;/p&gt;

&lt;p&gt;The final selection remains editorial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes a resource worth selecting?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm currently using a few simple questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it technically credible?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Primary sources, engineering blogs, research papers and serious open-source work get priority.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it solve a real engineering problem?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Observability, reliability, security, evaluations, context management, orchestration, infrastructure, cost...&lt;/p&gt;

&lt;p&gt;Not just demos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can someone apply something from it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even research becomes much more interesting when it changes how we think about architecture or implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it saying something new?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ten articles repeating the same product announcement don't deserve ten slots.&lt;/p&gt;

&lt;p&gt;Is it worth 10 minutes of someone's time?&lt;/p&gt;

&lt;p&gt;This may actually be the most important criterion.&lt;/p&gt;

&lt;p&gt;Attention is expensive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm currently preparing AI DevList #001, which will be published on August 23.&lt;/p&gt;

&lt;p&gt;Some of the topics I'm reviewing include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;emerging behavior in multi-agent systems,&lt;/li&gt;
&lt;li&gt;MCP architecture and security,&lt;/li&gt;
&lt;li&gt;testing agent skills,&lt;/li&gt;
&lt;li&gt;malicious skill files,&lt;/li&gt;
&lt;li&gt;agent observability,&lt;/li&gt;
&lt;li&gt;authorization for autonomous workflows,&lt;/li&gt;
&lt;li&gt;production evaluation patterns.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm intentionally keeping the first issue relatively small.&lt;/p&gt;

&lt;p&gt;I'd rather publish &lt;strong&gt;12 strong resources than 40 average ones.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is also an experiment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI DevList is a small project.&lt;/p&gt;

&lt;p&gt;The application itself is intentionally boring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;GitLab CI/CD&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interesting problem isn't the software.&lt;/p&gt;

&lt;p&gt;It's whether consistent technical curation can become useful enough that engineers return every week.&lt;/p&gt;

&lt;p&gt;Over the next few months, I want to learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which topics engineers actually click on?&lt;/li&gt;
&lt;li&gt;Do people prefer research or practical engineering articles?&lt;/li&gt;
&lt;li&gt;Are MCP resources still disproportionately interesting?&lt;/li&gt;
&lt;li&gt;How much context is enough before curation becomes commentary?&lt;/li&gt;
&lt;li&gt;Can a small, focused technical publication build a meaningful engineering audience?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have added click analytics from the beginning so I can answer some of those questions with data instead of intuition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're building AI systems&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can subscribe here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://aidevlist.io/" rel="noopener noreferrer"&gt;https://aidevlist.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first issue comes out on August 23.&lt;/p&gt;

&lt;p&gt;And if you publish a technical article, paper, open-source project or tool that you think AI engineers should see, there is also a submission page:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://aidevlist.io/submit" rel="noopener noreferrer"&gt;https://aidevlist.io/submit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm especially interested in feedback from engineers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What would make a curated AI engineering weekly genuinely useful enough for you to read every week?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>machinelearning</category>
      <category>productivity</category>
    </item>
    <item>
      <title>QualityHub v1.2.0 — Your CI now posts quality reports directly on your MRs</title>
      <dc:creator>Younes Ben Tlili</dc:creator>
      <pubDate>Wed, 25 Feb 2026 23:56:08 +0000</pubDate>
      <link>https://dev.to/younes_bentlili_9480340f/qualityhub-v120-your-ci-now-posts-quality-reports-directly-on-your-mrs-296</link>
      <guid>https://dev.to/younes_bentlili_9480340f/qualityhub-v120-your-ci-now-posts-quality-reports-directly-on-your-mrs-296</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/younes_bentlili_9480340f/i-built-a-cli-that-tells-you-if-your-release-is-safe-to-deploy-in-2-seconds-1dp4"&gt;my last article&lt;/a&gt;, I showed how qualityhub analyze gives you a risk score and go/no-go decision after every test run.&lt;br&gt;
At the end, I mentioned this was coming:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Auto-commenting on MRs — qualityhub analyze --comment posts directly on your GitLab MR or GitHub PR&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's shipped. Here's what it looks like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem with markdown reports&lt;/strong&gt;&lt;br&gt;
v1.1.0 already had --format markdown. You could generate a quality report and... do whatever you wanted with it. Most people saved it to a file and manually copied it somewhere.&lt;br&gt;
That's one step too many.&lt;br&gt;
The whole point of catching regressions is to surface them at the moment a developer is making a decision — when they're reviewing a merge request. Not buried in a CI log. Not in a separate tool. Right there, on the MR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What v1.2.0 does&lt;/strong&gt;&lt;br&gt;
One flag. That's it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qualityhub analyze --comment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your CI runs, parses the test results, calculates the risk score, and posts this directly on your GitLab MR or GitHub PR:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5isnnkii0l2sm46xwhm.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5isnnkii0l2sm46xwhm.jpeg" alt="GitLab merge request activity feed showing an automatic QualityHub comment with risk score 85/100 (LOW), status " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The comment is automatically updated on every pipeline run. No duplicate comments piling up on your MR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup: GitLab CI&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;quality-check:
  stage: test
  script:
    - npm test -- --coverage
    - npx qualityhub-cli parse jest ./coverage
    - npx qualityhub-cli analyze --comment
  cache:
    paths:
      - .qualityhub/   # history persists between runs → regression detection works
  only:
    - merge_requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set GITLAB_TOKEN as a CI/CD variable (needs api scope). Everything else — project ID, MR number — is auto-detected from GitLab's environment variables. No extra config.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup: GitHub Actions&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Run tests
  run: npm test -- --coverage

- name: QualityHub analysis
  run: |
    npx qualityhub-cli parse jest ./coverage
    npx qualityhub-cli analyze --comment
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GITHUB_TOKEN is already available in every GitHub Actions workflow. Nothing to configure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All the options&lt;/strong&gt;&lt;br&gt;
For teams with specific setups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Self-hosted GitLab
qualityhub analyze --comment \
  --api-url https://gitlab.yourcompany.com \
  --token $GITLAB_TOKEN

# Explicit project + MR (outside CI)
qualityhub analyze --comment \
  --provider gitlab \
  --token $GITLAB_TOKEN \
  --project-id 123 \
  --mr-id 42

# GitHub
qualityhub analyze --comment \
  --provider github \
  --token $GITHUB_TOKEN \
  --project-id owner/repo \
  --mr-id 42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters&lt;/strong&gt;&lt;br&gt;
When a comment appears directly on the MR, it changes the dynamic of code review.&lt;br&gt;
Instead of "tests passed, let's merge", reviewers now see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The exact risk score&lt;/li&gt;
&lt;li&gt;Whether coverage went up or down vs the previous run&lt;/li&gt;
&lt;li&gt;Any new test failures introduced by this branch&lt;/li&gt;
&lt;li&gt;A clear PROCEED / CAUTION / BLOCK decision&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The context is there at the moment the decision is made. Not one click away. Not in another tab. On the MR.&lt;br&gt;
This is what I was trying to build when I started QualityHub: &lt;strong&gt;quality intelligence at the right moment.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g qualityhub-cli

# Parse your test results
qualityhub parse jest ./coverage

# Post on your MR
qualityhub analyze --comment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/ybentlili/qualityhub-cli" rel="noopener noreferrer"&gt;ybentlili/qualityhub-cli&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/qualityhub-cli" rel="noopener noreferrer"&gt;qualityhub-cli&lt;/a&gt;&lt;br&gt;
MIT licensed. If this is useful, a ⭐ on GitHub goes a long way.&lt;/p&gt;

&lt;p&gt;Part 3 of building QualityHub in public. &lt;a href="https://dev.to/younes_bentlili_9480340f/i-built-qualityhub-ai-powered-quality-intelligence-for-your-releases-40d3"&gt;Part 1&lt;/a&gt; — &lt;a href="https://dev.to/younes_bentlili_9480340f/i-built-a-cli-that-tells-you-if-your-release-is-safe-to-deploy-in-2-seconds-1dp4"&gt;Part 2&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>opensource</category>
      <category>testing</category>
      <category>gitlab</category>
    </item>
    <item>
      <title>I built a CLI that tells you if your release is safe to deploy — in 2 seconds</title>
      <dc:creator>Younes Ben Tlili</dc:creator>
      <pubDate>Tue, 17 Feb 2026 22:53:38 +0000</pubDate>
      <link>https://dev.to/younes_bentlili_9480340f/i-built-a-cli-that-tells-you-if-your-release-is-safe-to-deploy-in-2-seconds-1dp4</link>
      <guid>https://dev.to/younes_bentlili_9480340f/i-built-a-cli-that-tells-you-if-your-release-is-safe-to-deploy-in-2-seconds-1dp4</guid>
      <description>&lt;p&gt;Every team has the same moment before deploying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Tests passed... coverage looks okay... should we ship it?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Someone eyeballs the numbers, checks if anything looks off, and says "yeah, let's go." No real process. No history. No way to catch a slow regression creeping in over weeks.&lt;br&gt;
I got tired of that. So I built&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qualityhub analyze
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it does&lt;/strong&gt;&lt;br&gt;
You run your tests. Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx qualityhub-cli parse jest ./coverage
npx qualityhub-cli analyze
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔍 QualityHub Analysis
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   Project:  my-app@2.3.1
   Branch:   main

   📊 Tests
      ❌ 243/250 passed (97.2%)
      ❌ 4 failed
      ⏭️  3 skipped
      ⏱️  Duration: 12.7s

   📈 Coverage
      Lines:      █████████████████░░░ 87.3%  ▼ -3.2%
      Branches:   ████████████████░░░░ 82.2%
      Functions:  ██████████████████░░ 91.3%

   🚨 Issues Detected
      🟡 4 tests failed (97.2% pass rate)
      🎲 2 flaky tests detected
      📉 Coverage dropped 3.2% since last run
      🆕 2 new test failures since last run
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   🎯 Risk Score:  ⚠️ 72/100 (MEDIUM RISK)
   📋 Decision:    ⚠️  CAUTION — Review issues before deploying
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A risk score from 0 to 100. A clear decision: PROCEED, CAUTION, or BLOCK. In 2 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem I was solving&lt;/strong&gt;&lt;br&gt;
At my previous job, we had 20 projects across 5 teams. Quality tracking was done in Excel files. Every sprint, someone would manually collect metrics from each team — test pass rates, coverage percentages, SonarQube results — paste them into a spreadsheet, and email it around.&lt;br&gt;
Nobody caught the slow regressions. Coverage would drop 0.5% per sprint, and after 6 months you'd realize you went from 85% to 70% without anyone noticing.&lt;br&gt;
I wanted something that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs in CI, not in someone's inbox&lt;/li&gt;
&lt;li&gt;Compares every run with the previous one automatically&lt;/li&gt;
&lt;li&gt;Catches what humans miss: slow drifts, new failures, build slowdowns&lt;/li&gt;
&lt;li&gt;Gives a clear answer, not a dashboard you have to interpret&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How the scoring works&lt;/strong&gt;&lt;br&gt;
The risk score starts at 100 and gets penalized:&lt;br&gt;
Test results (40 points max)&lt;br&gt;
Every failed test costs points. 2% failure rate? Small penalty. 10% failure rate? You're blocked.&lt;br&gt;
Coverage (30 points max)&lt;br&gt;
Below 80% line coverage? Penalty. Below 50%? Critical.&lt;br&gt;
Regressions (15 points max)&lt;br&gt;
This is where it gets interesting. The CLI stores history locally in .&lt;em&gt;&lt;strong&gt;qualityhub/history.json&lt;/strong&gt;&lt;/em&gt;. On every run, it compares with the last one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coverage dropped 3%? Flagged.&lt;/li&gt;
&lt;li&gt;2 new test failures? Flagged.&lt;/li&gt;
&lt;li&gt;Build time increased 20%? Flagged.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Issue severity (15 points max)&lt;br&gt;
Critical issues (security vulnerabilities, quality gate failures) cost more than warnings.&lt;br&gt;
The final score maps to a decision:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;85-100: PROCEED ✅&lt;/li&gt;
&lt;li&gt;65-84: CAUTION ⚠️&lt;/li&gt;
&lt;li&gt;40-64: HIGH RISK 🔶&lt;/li&gt;
&lt;li&gt;0-39: BLOCK 🛑&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;No server needed&lt;/strong&gt; &lt;br&gt;
This is the part I'm most proud of. Everything runs locally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;History is stored in &lt;em&gt;.qualityhub/history.json&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;No account to create&lt;/li&gt;
&lt;li&gt;No SaaS to connect&lt;/li&gt;
&lt;li&gt;No API keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g qualityhub-cli
qualityhub parse jest ./coverage
qualityhub analyze
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Value in 30 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI/CD integration&lt;/strong&gt;&lt;br&gt;
The CLI exits with code 1 when the decision is BLOCK. So you can use it as a quality gate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .gitlab-ci.yml
quality-check:
  stage: test
  script:
    - npm test -- --coverage
    - npx qualityhub-cli parse jest ./coverage
    - npx qualityhub-cli analyze
  cache:
    paths:
      - .qualityhub/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache persists the history between pipeline runs, so regression detection works across commits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Markdown reports for merge requests&lt;/strong&gt;&lt;br&gt;
You can also generate a Markdown report to post on your MR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qualityhub analyze --format markdown --output report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's next&lt;/strong&gt;&lt;br&gt;
I'm working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto-commenting on MRs&lt;/strong&gt; — qualityhub analyze --comment posts directly on your GitLab MR or GitHub PR&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More parsers&lt;/strong&gt; — pytest, XCTest, Go test&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-powered insights&lt;/strong&gt; — Using LLMs to explain why your quality is changing and what to fix first&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g qualityhub-cli

# Or test with example data:
git clone https://github.com/ybentlili/qualityhub-cli
cd qualityhub-cli
npm install &amp;amp;&amp;amp; npm run build &amp;amp;&amp;amp; npm link
qualityhub parse jest ./examples/jest
qualityhub analyze
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: ybentlili/qualityhub-cli&lt;br&gt;
npm: qualityhub-cli&lt;br&gt;
It's MIT licensed. Stars, feedback, and contributions are welcome.&lt;br&gt;
If you've ever looked at a CI pipeline and thought "I think we can ship this" — this is for you.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>opensource</category>
      <category>testing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Built QualityHub: AI-Powered Quality Intelligence for Your Releases</title>
      <dc:creator>Younes Ben Tlili</dc:creator>
      <pubDate>Sun, 01 Feb 2026 11:10:45 +0000</pubDate>
      <link>https://dev.to/younes_bentlili_9480340f/i-built-qualityhub-ai-powered-quality-intelligence-for-your-releases-40d3</link>
      <guid>https://dev.to/younes_bentlili_9480340f/i-built-qualityhub-ai-powered-quality-intelligence-for-your-releases-40d3</guid>
      <description>&lt;h1&gt;
  
  
  🎯 The Problem
&lt;/h1&gt;

&lt;p&gt;As a developer , I faced this question every day:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Can we ship this release to production?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We had test results, coverage metrics, SonarQube reports... but &lt;strong&gt;no single source of truth&lt;/strong&gt; to answer this simple question.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;QualityHub&lt;/strong&gt; - an AI-powered platform that analyzes your quality metrics and gives you instant go/no-go decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 What is QualityHub?
&lt;/h2&gt;

&lt;p&gt;QualityHub is an &lt;strong&gt;open-source quality intelligence platform&lt;/strong&gt; that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;📊 &lt;strong&gt;Aggregates&lt;/strong&gt; test results from any framework (Jest, JUnit, JaCoCo...)&lt;/li&gt;
&lt;li&gt;🤖 &lt;strong&gt;Analyzes&lt;/strong&gt; quality metrics with AI&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Decides&lt;/strong&gt; if you can ship to production&lt;/li&gt;
&lt;li&gt;📈 &lt;strong&gt;Tracks&lt;/strong&gt; trends over time in a beautiful dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: TypeScript + Express + PostgreSQL + Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: Next.js 14 + Tailwind CSS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI&lt;/strong&gt;: TypeScript with parsers for Jest, JaCoCo, JUnit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: Docker Compose (self-hostable)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: MIT&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💡 How It Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Universal Format: &lt;code&gt;qa-result.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of forcing you to use specific tools, QualityHub uses an &lt;strong&gt;open standard format&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"project"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.3.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a3f4d2c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"branch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-31T14:30:00Z"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1247&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"passed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1245&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"failed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"skipped"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"duration_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45230&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"flaky_tests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"UserAuthTest.testTimeout"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"coverage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;87.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"branches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;82.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"functions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;91.2&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This format works with &lt;strong&gt;any test framework&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. CLI Parsers
&lt;/h3&gt;

&lt;p&gt;The CLI automatically converts your test results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Jest (JavaScript/TypeScript)&lt;/span&gt;
qualityhub parse jest ./coverage

&lt;span class="c"&gt;# JaCoCo (Java)&lt;/span&gt;
qualityhub parse jacoco ./target/site/jacoco/jacoco.xml

&lt;span class="c"&gt;# JUnit (Java/Kotlin/Python)&lt;/span&gt;
qualityhub parse junit ./build/test-results/test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Risk Analysis Engine
&lt;/h3&gt;

&lt;p&gt;The backend analyzes your results and calculates a &lt;strong&gt;Risk Score&lt;/strong&gt; (0-100):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Risk factors analyzed:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="nx"&gt;pass&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Code&lt;/span&gt; &lt;span class="nf"&gt;coverage &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Flaky&lt;/span&gt; &lt;span class="nx"&gt;tests&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Coverage&lt;/span&gt; &lt;span class="nx"&gt;trends&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Code&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt; &lt;span class="nf"&gt;metrics &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SAFE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"decision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PROCEED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reasoning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Test pass rate: 99.8%. Coverage: 87.3%. No critical issues."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"recommendations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Beautiful Dashboard
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
![ ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zx8uj4p9okks9a4ohq7h.png)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Track metrics over time, see trends, and make informed decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Self-Hosted (5 minutes)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone repo&lt;/span&gt;
git clone https://github.com/ybentlili/qualityhub.git
&lt;span class="nb"&gt;cd &lt;/span&gt;qualityhub

&lt;span class="c"&gt;# Start everything with Docker&lt;/span&gt;
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# ✅ Backend: http://localhost:8080&lt;/span&gt;
&lt;span class="c"&gt;# ✅ Frontend: http://localhost:3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use the CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; qualityhub-cli

&lt;span class="c"&gt;# Initialize&lt;/span&gt;
qualityhub init

&lt;span class="c"&gt;# Parse your test results&lt;/span&gt;
qualityhub parse jest ./coverage

&lt;span class="c"&gt;# Push to QualityHub&lt;/span&gt;
qualityhub push qa-result.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Done!&lt;/strong&gt; Your metrics appear in the dashboard instantly.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Why I Built This
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Pain Points
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fragmented Tools&lt;/strong&gt;: Jest for tests, JaCoCo for coverage, SonarQube for quality... each tool has its own UI and format.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No Single Answer&lt;/strong&gt;: "Can we ship?" required checking 5 different tools and making a gut decision.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No History&lt;/strong&gt;: Hard to track quality trends over time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual Process&lt;/strong&gt;: No automation, no CI/CD integration.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;QualityHub &lt;strong&gt;aggregates everything&lt;/strong&gt; into one dashboard and uses &lt;strong&gt;AI to make the decision&lt;/strong&gt; for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐
│     CLI     │ ← Parse test results
└──────┬──────┘
       │ POST /api/v1/results
       ↓
┌─────────────────────────────┐
│     Backend (API)           │
│  • Express + TypeScript     │
│  • PostgreSQL + Redis       │
│  • Risk Analysis Engine     │
└──────────────┬──────────────┘
               │
               ↓
┌─────────────────────────────┐
│   Frontend (Dashboard)      │
│  • Next.js 14               │
│  • Real-time metrics        │
└─────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📊 Technical Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Parser Architecture
&lt;/h3&gt;

&lt;p&gt;Each parser extends a base class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseParser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;buildBaseResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;adapterName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projectInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GIT_COMMIT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Auto-detect CI/CD environment&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;ci_provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectCIProvider&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;adapters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;adapterName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes &lt;strong&gt;adding new parsers trivial&lt;/strong&gt;. Want pytest support? Extend &lt;code&gt;BaseParser&lt;/code&gt; and implement &lt;code&gt;parse()&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Risk Scoring Algorithm (MVP)
&lt;/h3&gt;

&lt;p&gt;The current version uses &lt;strong&gt;rule-based scoring&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Test failures&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Coverage thresholds&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coverage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Flaky tests&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flakyTests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;flakyTests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Ensure 0-100 range&lt;/span&gt;
&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Future&lt;/strong&gt;: Replace with AI-powered analysis (Claude API) for contextual insights.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Database Schema
&lt;/h3&gt;

&lt;p&gt;Simple and efficient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;projects&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;qa_results&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;project_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;version&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;commit&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;branch&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;metrics&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;-- Flexible JSON storage&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;risk_analyses&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;qa_result_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;qa_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;risk_score&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;reasoning&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;risks&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;recommendations&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;JSONB&lt;/strong&gt; allows flexible metric storage without schema migrations.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What's Next?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  v1.1 (Planned)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🤖 &lt;strong&gt;AI-Powered Analysis&lt;/strong&gt; with Claude API&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Trend Detection&lt;/strong&gt; (coverage dropping over time)&lt;/li&gt;
&lt;li&gt;🔔 &lt;strong&gt;Slack/Email Notifications&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🔌 &lt;strong&gt;GitHub App&lt;/strong&gt; (comments on PRs)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  v1.2 (Future)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📈 &lt;strong&gt;Advanced Analytics&lt;/strong&gt; (benchmarking, predictions)&lt;/li&gt;
&lt;li&gt;🔐 &lt;strong&gt;SSO &amp;amp; RBAC&lt;/strong&gt; for enterprise&lt;/li&gt;
&lt;li&gt;🌍 &lt;strong&gt;Multi-language support&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🎨 &lt;strong&gt;Custom dashboards&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Open Standards Win
&lt;/h3&gt;

&lt;p&gt;Making &lt;code&gt;qa-result.json&lt;/code&gt; an &lt;strong&gt;open standard&lt;/strong&gt; was key. Now anyone can build parsers or integrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Developer Experience Matters
&lt;/h3&gt;

&lt;p&gt;The CLI &lt;strong&gt;must be dead simple&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qualityhub parse jest ./coverage  &lt;span class="c"&gt;# Just works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No config files, no setup, just works.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Self-Hosting is a Feature
&lt;/h3&gt;

&lt;p&gt;Many companies &lt;strong&gt;can't&lt;/strong&gt; send their metrics to external SaaS. Docker Compose makes self-hosting trivial.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤝 Contributing
&lt;/h2&gt;

&lt;p&gt;QualityHub is &lt;strong&gt;100% open-source&lt;/strong&gt; (MIT License).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to contribute?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧪 &lt;strong&gt;Add parsers&lt;/strong&gt; (pytest, XCTest, Rust...)&lt;/li&gt;
&lt;li&gt;🎨 &lt;strong&gt;Improve the dashboard&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Fix bugs&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Write docs&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out: &lt;a href="https://github.com/ybentlili/qualityhub/blob/master/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Contributing Guide&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/ybentlili/qualityhub" rel="noopener noreferrer"&gt;ybentlili/qualityhub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI&lt;/strong&gt;: &lt;a href="https://github.com/ybentlili/qualityhub-cli" rel="noopener noreferrer"&gt;qualityhub-cli&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/qualityhub-cli" rel="noopener noreferrer"&gt;qualityhub-cli&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Try It Now
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Self-host in 5 minutes&lt;/span&gt;
git clone https://github.com/ybentlili/qualityhub.git
&lt;span class="nb"&gt;cd &lt;/span&gt;qualityhub
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Or just the CLI&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; qualityhub-cli
qualityhub parse jest ./coverage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  💬 What do you think?
&lt;/h2&gt;

&lt;p&gt;Would you use this? What features would you like to see?&lt;/p&gt;

&lt;p&gt;Drop a ⭐ on &lt;a href="https://github.com/ybentlili/qualityhub" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; if you find this useful!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Built with ❤️ in TypeScript&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>opensource</category>
      <category>typescript</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
