<?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.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>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>
