<?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: Komal deep</title>
    <description>The latest articles on DEV Community by Komal deep (@komal_deep_355).</description>
    <link>https://dev.to/komal_deep_355</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%2F4047872%2F8ff91215-5a7e-4982-a0f0-d213cfb89ee7.png</url>
      <title>DEV Community: Komal deep</title>
      <link>https://dev.to/komal_deep_355</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/komal_deep_355"/>
    <language>en</language>
    <item>
      <title>“I’ll Do It Tomorrow” Is Predictable, So I Built a Model to Predict It</title>
      <dc:creator>Komal deep</dc:creator>
      <pubDate>Mon, 26 Oct 2026 06:57:27 +0000</pubDate>
      <link>https://dev.to/komal_deep_355/ill-do-it-tomorrow-is-predictable-so-i-built-a-model-to-predict-it-333j</link>
      <guid>https://dev.to/komal_deep_355/ill-do-it-tomorrow-is-predictable-so-i-built-a-model-to-predict-it-333j</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I have been a procrastinator for as long as I can remember. It's a habit that feels baked into me from the start.&lt;/p&gt;

&lt;p&gt;I've tried the standard fixes. The 15-minute rule: just start, and you can stop after fifteen minutes. Timers. Announcing my plans out loud so someone would hold me to them. Honestly, they work. I get started, I stay on task, and I finish things I would once have avoided for weeks.&lt;/p&gt;

&lt;p&gt;I still procrastinate, though. What changed is the amount, not the habit. And the habit has cost me real things: missed deadlines, and some opportunities that never came back.&lt;/p&gt;

&lt;p&gt;At some point I noticed something strange. Every productivity tool I used tracked what I &lt;em&gt;finished&lt;/em&gt;. None of them told me what I was &lt;em&gt;about to postpone&lt;/em&gt;. All the effort went into the plan, and none went into asking whether the plan would survive a bad day.&lt;/p&gt;

&lt;p&gt;That's how &lt;strong&gt;Slothcast&lt;/strong&gt; 🦥 started. It's a deadline-risk forecast for your own planned work. Each night you log what you plan to do tomorrow, with the duration, how close the deadline is, and how much energy you expect to have. The next day you record whether it happened. Slothcast learns from that history and tells you the chance that tomorrow's plan slips. Then it shrinks the plan, so a bad day still counts for something.&lt;/p&gt;

&lt;p&gt;Weather forecasts don't stop the rain, but they tell you to carry an umbrella. A procrastination forecast should do the same for your calendar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://slothcast.onrender.com" rel="noopener noreferrer"&gt;https://slothcast.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app opens on synthetic demo data until you have a real log, and it flags that data clearly so a demo number is never mistaken for your real behavior. (It's on a free Render instance, so the first load may take a few seconds to wake up.)&lt;/p&gt;

&lt;p&gt;You can also run it locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
make &lt;span class="nb"&gt;install
&lt;/span&gt;make &lt;span class="nb"&gt;test
&lt;/span&gt;make sample        &lt;span class="c"&gt;# synthetic demo log, clearly tagged as synthetic&lt;/span&gt;
make eval-sample   &lt;span class="c"&gt;# chronological evaluation, written to results/&lt;/span&gt;
make demo          &lt;span class="c"&gt;# opens the dashboard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use it for real:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;slothcast log &lt;span class="nt"&gt;--task&lt;/span&gt; &lt;span class="s2"&gt;"DSA block"&lt;/span&gt; &lt;span class="nt"&gt;--minutes&lt;/span&gt; 120 &lt;span class="nt"&gt;--deadline-days&lt;/span&gt; 3 &lt;span class="nt"&gt;--energy&lt;/span&gt; 3
slothcast &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="nt"&gt;--date&lt;/span&gt; 2026-10-22 &lt;span class="nt"&gt;--done&lt;/span&gt; 1
slothcast &lt;span class="nb"&gt;eval
&lt;/span&gt;slothcast forecast &lt;span class="nt"&gt;--task&lt;/span&gt; &lt;span class="s2"&gt;"DSA block"&lt;/span&gt; &lt;span class="nt"&gt;--minutes&lt;/span&gt; 120 &lt;span class="nt"&gt;--deadline-days&lt;/span&gt; 3 &lt;span class="nt"&gt;--energy&lt;/span&gt; 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/KomalDeep355/Slothcast" rel="noopener noreferrer"&gt;https://github.com/KomalDeep355/Slothcast&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://slothcast.onrender.com" rel="noopener noreferrer"&gt;https://slothcast.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MIT licensed, with about 40 tests and a Makefile.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Forecasting, not coaching
&lt;/h3&gt;

&lt;p&gt;Most "AI productivity" apps hand your to-do list to a chatbot and ask it for advice. I wanted the opposite. Slothcast puts a real, testable prediction at the center, and the language model only helps explain it. That one decision shaped everything else, and it's why I wrote the rules below into the code instead of only promising them in a README.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data, labeled by how much I trust it
&lt;/h3&gt;

&lt;p&gt;The log is a simple CSV: date, planned task, planned minutes, days to deadline, energy (1-5), and whether it was completed. The labels come from three places:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A nightly check-in.&lt;/strong&gt; I plan tomorrow in the evening and record the outcome the next day. These labels are marked &lt;code&gt;verified&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub commits.&lt;/strong&gt; An optional ingester counts my commits. If I left a day blank, commits can &lt;em&gt;infer&lt;/em&gt; an outcome, but that label is tagged &lt;code&gt;inferred&lt;/code&gt; and never mixed up with a verified one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backfilled history.&lt;/strong&gt; I can import old milestones from a CSV. These default to &lt;code&gt;inferred&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Demo data is tagged &lt;code&gt;synthetic&lt;/code&gt;. Every label keeps its provenance, and the evaluation also reports results on verified labels alone whenever there are enough of them. I didn't want my model quietly learning from guesses I couldn't trust.&lt;/p&gt;

&lt;h3&gt;
  
  
  No leakage from the future
&lt;/h3&gt;

&lt;p&gt;For a forecasting project, the easiest way to cheat is to let the future leak into the past. In Slothcast, the features for day &lt;em&gt;i&lt;/em&gt; only see outcomes from &lt;em&gt;before&lt;/em&gt; day &lt;em&gt;i&lt;/em&gt;. Evaluation is rolling-origin: train on the past, test on what comes next, and move forward. Nothing is shuffled. Both rules are unit-tested, because a shuffled split on time-ordered habit data gives you beautiful, meaningless numbers.&lt;/p&gt;

&lt;h3&gt;
  
  
  The data-sufficiency gate
&lt;/h3&gt;

&lt;p&gt;This is the part I'm proudest of. A model trained on five days of your life isn't a forecaster, it's a coin flip with a confident voice. So Slothcast has a gate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under 15 labelled days (or fewer than 4 of either outcome), it shows &lt;strong&gt;"Not enough history yet"&lt;/strong&gt; instead of a probability.&lt;/li&gt;
&lt;li&gt;15 to 29 days is labelled &lt;strong&gt;preliminary&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Showing nothing is a feature here. A tool that always has an answer isn't being honest.&lt;/p&gt;

&lt;h3&gt;
  
  
  TabPFN versus honest baselines
&lt;/h3&gt;

&lt;p&gt;The model at the center of the project is &lt;strong&gt;TabPFN&lt;/strong&gt;, a pretrained model built for small tabular datasets, which is exactly what a personal habit log is. A few weeks of check-ins is tiny data. Classic models struggle with that, and a pretrained model might help.&lt;/p&gt;

&lt;p&gt;But I didn't want to assume it. Slothcast compares TabPFN against five baselines: the base rate, "same as yesterday", the weekday rate, logistic regression, and a random forest. TabPFN has to beat them. The evaluation reports the Brier score, calibration, a learning curve by history length (how much data does the fancy model need before it's worth it?), and a bootstrap interval for the gain over the base rate. If TabPFN doesn't win, the report says so.&lt;/p&gt;

&lt;p&gt;TabPFN runs locally. The first fit downloads the open weights, and after that it works offline.&lt;/p&gt;

&lt;h3&gt;
  
  
  A local Gemma planner
&lt;/h3&gt;

&lt;p&gt;Once the forecast gives a risk label, a local &lt;strong&gt;Gemma&lt;/strong&gt; model (&lt;code&gt;gemma3:4b&lt;/code&gt; through Ollama) turns it into a plan. A risky task becomes a smaller, concrete first step. If Ollama isn't running, Slothcast falls back to a deterministic template plan and tells you it did.&lt;/p&gt;

&lt;h3&gt;
  
  
  The LLM is never allowed to write a number
&lt;/h3&gt;

&lt;p&gt;This is my favorite guardrail. &lt;code&gt;agent/guardrails.py&lt;/code&gt; rejects any percentage, decimal probability, or odds that the model didn't get from the forecasting model. If Gemma invents a "73% chance", the output is thrown away and the template plan is used instead.&lt;/p&gt;

&lt;p&gt;A language model that confidently makes up statistics about &lt;em&gt;your&lt;/em&gt; behavior is worse than useless, because you'd believe it. The forecast comes from a model that was tested. The words come from a model that was fenced in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optional pieces
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A hosted habit coach&lt;/strong&gt; for any OpenAI-compatible provider. It only sends aggregate completion counts, rates, and pattern groups, never task names, and only when you ask for coaching. Without it, the numerical habit forecasts still work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporal&lt;/strong&gt; to run the nightly job as a durable workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sentry&lt;/strong&gt; tracing, where spans carry timings and token counts but never task text.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Python, Pandas, NumPy, scikit-learn, TabPFN, Gemma via Ollama, Streamlit, Pytest, GitHub Actions, and an MIT license. Temporal and Sentry are optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The hard part of a forecasting project isn't the model.&lt;/strong&gt; It was keeping the future out of the past, keeping guessed labels apart from real ones, and deciding when to say "I don't know". The model was a small part of the work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small data needs honesty more than cleverness.&lt;/strong&gt; With a few dozen data points, a sophisticated model can look great by accident. The learning curve and the bootstrap interval are there to stop me from fooling myself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraining the LLM made the product better.&lt;/strong&gt; Letting Gemma speak freely was easy. Fencing it in, so it explains evidence but never produces it, is what made the app trustworthy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's a mirror, not a judge.&lt;/strong&gt; I started this to fix my own procrastination. What I ended up with is a way to see my patterns without the shame that usually comes with them.&lt;/p&gt;

&lt;p&gt;One thing Slothcast doesn't claim is that its nudges change behavior. Showing that would need an A/B period of nudged versus not-nudged weeks, which only makes sense once the forecast itself works. That's the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;A procrastination log is some of the most personal data a person can produce. It records when you avoid things, how tired you were, and what you keep putting off. I wasn't willing to send that to a closed API.&lt;/p&gt;

&lt;p&gt;Open models made a local-first design possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma runs on my machine&lt;/strong&gt; through Ollama, so my task history stays there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TabPFN runs offline&lt;/strong&gt; after the first download of the open weights, so the forecasting is private too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run.&lt;/strong&gt; No API bill, no usage limit, no account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could test it fairly.&lt;/strong&gt; Because the models are open and local, I could benchmark them against simple baselines and report whatever came out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One honest note: TabPFN's weights are under Prior Labs' non-commercial license, while the code in my repo is MIT. That's fine for research, evaluation, and a portfolio, but not for a commercial product. I'd rather say that plainly than hide it.&lt;/p&gt;

&lt;p&gt;Open innovation also means anyone can fork Slothcast and swap in their own model, their own labels, or their own habits. A procrastination forecaster should be something you can inspect, not something you have to trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Gemma, TabPFN
&lt;/h2&gt;

&lt;p&gt;If you've ever missed something important because you waited "just a little longer", you're not lazy. You're predictable, and predictable can be forecast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the one task you keep postponing? Tell me in the comments. I'm curious whether the forecast would have caught it.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>🚨 What breaks when your best engineer leaves? HandoffGraph uses AI and knowledge graphs to uncover hidden dependencies, identify knowledge gaps, and plan smoother handoffs. Because your code should never depend on just one person!</title>
      <dc:creator>Komal deep</dc:creator>
      <pubDate>Mon, 19 Oct 2026 14:16:53 +0000</pubDate>
      <link>https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-handoffgraph-uses-ai-and-knowledge-graphs-to-uncover-1ggc</link>
      <guid>https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-handoffgraph-uses-ai-and-knowledge-graphs-to-uncover-1ggc</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-i-asked-a-graph-4khg" class="crayons-story__hidden-navigation-link"&gt;What Breaks When Your Best Engineer Leaves? I Asked a Graph&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/komal_deep_355" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4047872%2F8ff91215-5a7e-4982-a0f0-d213cfb89ee7.png" alt="komal_deep_355 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/komal_deep_355" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Komal deep
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Komal deep
                
                
              
              &lt;div id="story-author-preview-content-4873522" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/komal_deep_355" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4047872%2F8ff91215-5a7e-4982-a0f0-d213cfb89ee7.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Komal deep&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-i-asked-a-graph-4khg" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-i-asked-a-graph-4khg" id="article-link-4873522"&gt;
          What Breaks When Your Best Engineer Leaves? I Asked a Graph
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/python"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;python&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-i-asked-a-graph-4khg" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-i-asked-a-graph-4khg#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>career</category>
      <category>management</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What Breaks When Your Best Engineer Leaves? I Asked a Graph</title>
      <dc:creator>Komal deep</dc:creator>
      <pubDate>Sun, 18 Oct 2026 18:17:14 +0000</pubDate>
      <link>https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-i-asked-a-graph-4khg</link>
      <guid>https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-i-asked-a-graph-4khg</guid>
      <description>&lt;p&gt;-&amp;gt; &lt;strong&gt;TL;DR&lt;/strong&gt; HandoffGraph loads a repository's Git history into FalkorDB, then an agent walks the graph to answer &lt;em&gt;"what breaks if this person leaves, and who can take over?"&lt;/em&gt; Every answer comes with the Cypher it ran and a live subgraph. The agent then &lt;strong&gt;acts&lt;/strong&gt;: it writes a hand-off plan.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💻 &lt;strong&gt;Code:&lt;/strong&gt; [github.com/KomalDeep355/HandoffGraph]&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/KomalDeep355" rel="noopener noreferrer"&gt;
        KomalDeep355
      &lt;/a&gt; / &lt;a href="https://github.com/KomalDeep355/HandoffGraph" rel="noopener noreferrer"&gt;
        HandoffGraph
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;HandoffGraph&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;An agent that finds the files only one person understands, and plans the hand-off before that person leaves.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HandoffGraph loads a repository's Git history into &lt;strong&gt;FalkorDB&lt;/strong&gt; as a knowledge graph of contributors, files and modules. An agent then calls graph tools to answer questions such as &lt;em&gt;which modules depend on one person&lt;/em&gt;, &lt;em&gt;what is orphaned if they leave&lt;/em&gt;, and &lt;em&gt;who should inherit it&lt;/em&gt;. It acts on the answer by writing a hand-off plan. Every answer ships with the Cypher that ran and the subgraph that justified it.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.python.org/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ac095efffec6ac483a78a54200d72e16c1eeeb3c29542653715e7dd2eb56ef00/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f507974686f6e2d332e31302532422d626c7565" alt="Python"&gt;&lt;/a&gt;
&lt;a href="https://fastapi.tiangolo.com/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0b0fee8d41e67504a669cc7962a45fbe4682b4b11f15e6c9e4f38cc079e8b5ad/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4150492d466173744150492d303039363838" alt="FastAPI"&gt;&lt;/a&gt;
&lt;a href="https://www.falkordb.com/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c0a767d2d1bac5b3226405998240374e64b3764c01abe02bd714aadddf6482ae/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f47726170685f44422d46616c6b6f7244422d374236314646" alt="FalkorDB"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://handoffgraph.onrender.com/" rel="nofollow noopener noreferrer"&gt;https://handoffgraph.onrender.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blog post:&lt;/strong&gt; &lt;a href="https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-i-asked-a-graph-4khg" rel="nofollow"&gt;https://dev.to/komal_deep_355/what-breaks-when-your-best-engineer-leaves-i-asked-a-graph-4khg&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Video link:&lt;/strong&gt; &lt;a href="https://youtu.be/zoFgDCOhXvQ" rel="nofollow noopener noreferrer"&gt;https://youtu.be/zoFgDCOhXvQ&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The problem&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Important knowledge in a codebase often sits with one person. A module may have a single recent author, or a file may have no active alternative owner. When that person is unavailable, the team finds out the hard way. Static CODEOWNERS files don't show who has actually worked…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/KomalDeep355/HandoffGraph" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
🌐 &lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://handoffgraph.onrender.com" rel="noopener noreferrer"&gt;https://handoffgraph.onrender.com&lt;/a&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where the idea came from
&lt;/h2&gt;

&lt;p&gt;It started with an Instagram reel. I don't remember the names and I can't verify the story, but the gist stuck with me: a company's code depended almost entirely on one developer, because he was the only person who really understood what it was doing. When he left, the team was stuck enough that they had to call him back.&lt;/p&gt;

&lt;p&gt;It was told as a joke, and it's funny until it happens to your team. It made me wonder whether you could spot that risk &lt;em&gt;before&lt;/em&gt; someone leaves, using the history your repository already has.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question nobody can answer quickly
&lt;/h2&gt;

&lt;p&gt;Imagine your most experienced maintainer announces a long break, starting Monday.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Which parts of the codebase can nobody else touch?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On most teams the honest answer is a shrug. A &lt;code&gt;CODEOWNERS&lt;/code&gt; file tells you who is &lt;em&gt;listed&lt;/em&gt;, not who has actually worked on a file recently, and it says nothing about whether anyone else could pick it up. This is &lt;strong&gt;key-person risk&lt;/strong&gt;, better known as the bus factor, and the evidence for it is already sitting in your repository history.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: Git history is already a graph
&lt;/h2&gt;

&lt;p&gt;Every commit says &lt;em&gt;this person changed these files at this time&lt;/em&gt;. Add the module each file lives in, and which files tend to change together, and you have a network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(Contributor) --TOUCHED--&amp;gt; (File) --IN_MODULE--&amp;gt; (Module)
      |                       |
      |                  CO_CHANGED  (files that change together)
      |
  COLLABORATES --&amp;gt; (Contributor)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Asking "what breaks if X leaves?" means following those edges: from a person to their files, from those files to &lt;em&gt;other active people&lt;/em&gt;, and from the files up to their modules. That is a traversal. In a relational table, each new follow-up question is more joins and more glue code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does the graph really do the work?
&lt;/h2&gt;

&lt;p&gt;The hackathon brief says the graph has to shape what the agent does, not sit on the side. In HandoffGraph, the agent knows nothing about the repository except what FalkorDB returns. Take the database away and every tool has nothing to say.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The agent needs to decide...&lt;/th&gt;
&lt;th&gt;FalkorDB feature it uses&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Which modules are fragile&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;KNOWS&lt;/code&gt; edges aggregated from &lt;code&gt;TOUCHED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What is orphaned if X leaves&lt;/td&gt;
&lt;td&gt;Multi-hop &lt;code&gt;OPTIONAL MATCH&lt;/code&gt;, restricted to &lt;em&gt;active&lt;/em&gt; peers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who should take over&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CO_CHANGED&lt;/code&gt; proximity plus direct ownership&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;em&gt;Why&lt;/em&gt; it reached a conclusion&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;shortestPath&lt;/code&gt; from person to module&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who the hubs are&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;algo.pageRank&lt;/code&gt; on &lt;code&gt;COLLABORATES&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keeping repos separate&lt;/td&gt;
&lt;td&gt;One graph per repository (multi-graph)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An honest note on the split: traversals and aggregations run in Cypher. The last bit of ranking arithmetic, like "how many people cover 50% of the knowledge", runs in Python on the rows Cypher returns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three queries that carry the project
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. The orphan check.&lt;/strong&gt; Start at a person, walk to every file they touched, and look for &lt;em&gt;other active contributors&lt;/em&gt; on the same files. Files with no real backup are the risk.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;p:&lt;/span&gt;&lt;span class="n"&gt;Contributor&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;id:&lt;/span&gt;&lt;span class="n"&gt;$id&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="py"&gt;t:&lt;/span&gt;&lt;span class="n"&gt;TOUCHED&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;f:&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;OPTIONAL&lt;/span&gt; &lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="py"&gt;o:&lt;/span&gt;&lt;span class="n"&gt;TOUCHED&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;q:&lt;/span&gt;&lt;span class="n"&gt;Contributor&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;q.id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;$id&lt;/span&gt; &lt;span class="ow"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;q.active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.score&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;mine&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;coalesce&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o.score&lt;/span&gt;&lt;span class="ss"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;others&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;active_peers&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;mine&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt; &lt;span class="ow"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;others&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;mine&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;f.path&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f.module&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mine&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;others&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active_peers&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;mine&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. The explanation.&lt;/strong&gt; Instead of "trust me", the agent can return the actual chain of evidence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;a:&lt;/span&gt;&lt;span class="n"&gt;Contributor&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;id:&lt;/span&gt;&lt;span class="n"&gt;$id&lt;/span&gt;&lt;span class="ss"&gt;}),&lt;/span&gt; &lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;m:&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;name:&lt;/span&gt;&lt;span class="n"&gt;$m&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="nf"&gt;shortestPath&lt;/span&gt;&lt;span class="ss"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:TOUCHED&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;IN_MODULE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;..3&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="ss"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. The hubs.&lt;/strong&gt; Graph algorithms as agent tools, not code the agent has to write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;algo.pageRank&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Contributor'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'COLLABORATES'&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;YIELD&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;node.name&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node.active&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Successor ranking (direct recent work plus &lt;code&gt;CO_CHANGED&lt;/code&gt; proximity) and module risk are in the &lt;a href="https://github.com/KomalDeep355/HandoffGraph#cypher-queries-and-graph-algorithms-the-product-depends-on" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I found on a real repository
&lt;/h2&gt;

&lt;p&gt;I loaded the public history of &lt;code&gt;fastapi/fastapi&lt;/code&gt; (source files under &lt;code&gt;fastapi/&lt;/code&gt; only): &lt;strong&gt;855 commits, 209 contributors, 59 files, 7 modules&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;6 of the 7 modules have a bus factor of 1 under my heuristic.&lt;/li&gt;
&lt;li&gt;One contributor holds about 80% of the recent-weighted knowledge in the core package.&lt;/li&gt;
&lt;li&gt;17 files across modules have no other active contributor with comparable recent history.&lt;/li&gt;
&lt;li&gt;The agent explains the link with a path such as &lt;code&gt;Person → fastapi/__init__.py → fastapi/(core)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I want to be careful here. That is a statement about &lt;strong&gt;commit history&lt;/strong&gt;, not about the project's health. FastAPI has many maintainers and reviewers, and review work, triage and design discussion don't show up in commits. I chose a well-known public repo because anyone can verify it, not because I think it is at risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  From insight to action
&lt;/h2&gt;

&lt;p&gt;Track 01 is about agents that &lt;em&gt;act&lt;/em&gt; on what the graph shows. Ask for a hand-off plan and the agent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;runs the leave-impact query for that person,&lt;/li&gt;
&lt;li&gt;finds successors for the most affected modules,&lt;/li&gt;
&lt;li&gt;writes a Markdown plan, like this:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## fastapi/(core)  (80% of module knowledge, 7/26 files orphaned)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Suggested mentee: &lt;span class="gs"&gt;**&amp;lt;top-ranked active contributor&amp;gt;**&lt;/span&gt; (backup: &lt;span class="nt"&gt;&amp;lt;next&lt;/span&gt; &lt;span class="na"&gt;candidate&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; Pair-review these files first: &lt;span class="sb"&gt;`fastapi/routing.py`&lt;/span&gt;, &lt;span class="sb"&gt;`fastapi/params.py`&lt;/span&gt;, ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can also draft GitHub issues. Those are a &lt;strong&gt;dry run&lt;/strong&gt; unless you set a token, point it at &lt;em&gt;your own sandbox repo&lt;/em&gt;, and tick a checkbox, because an agent should never open issues on someone else's project by surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decisions I'd defend
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recency matters.&lt;/strong&gt; Each commit-file pair decays with a one-year half-life, so last month's work counts more than work from years ago.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mass commits are noise.&lt;/strong&gt; Merge commits, bots and commits touching more than 40 files are dropped. A repo-wide reformat says little about who understands the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy by default.&lt;/strong&gt; Author emails are hashed. Only public Git metadata is used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Show your work.&lt;/strong&gt; The UI lists every tool call with its Cypher beside the subgraph it used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same tools, three doors.&lt;/strong&gt; A built-in agent (any OpenAI-compatible LLM, or an offline rule-based planner with no API key), an &lt;strong&gt;MCP server&lt;/strong&gt; exposing the same graph tools, and a small web UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it can't tell you
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Commit counts are not expertise. A reviewer who never commits is invisible.&lt;/li&gt;
&lt;li&gt;One person with two commit emails shows up twice.&lt;/li&gt;
&lt;li&gt;Scores and thresholds are heuristics, not calibrated probabilities.&lt;/li&gt;
&lt;li&gt;For a module one person owns completely, successor suggestions fall back to weak co-change links.&lt;/li&gt;
&lt;li&gt;I haven't tested it on very large repositories.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://github.com/KomalDeep355/HandoffGraph.git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HandoffGraph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\.venv\Scripts\Activate.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;requirements.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\scripts\run.ps1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ingest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\scripts\run.ps1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;serve&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8000&lt;/code&gt; and ask: &lt;em&gt;"What happens if the top contributor leaves?"&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Add review and issue data to the graph so reviewers stop being invisible, merge contributor identities, and track how risk changes over time instead of taking one snapshot.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Built Redmargin because I couldn’t tell if my projects matched the roles I’m applying for. Paste a repo + target role, get a blunt verdict against real job postings. Try it on your own repo and tell me where it’s wrong.</title>
      <dc:creator>Komal deep</dc:creator>
      <pubDate>Sun, 18 Oct 2026 14:35:07 +0000</pubDate>
      <link>https://dev.to/komal_deep_355/built-redmargin-because-i-couldnt-tell-if-my-projects-matched-the-roles-im-applying-for-paste-a-5h0</link>
      <guid>https://dev.to/komal_deep_355/built-redmargin-because-i-couldnt-tell-if-my-projects-matched-the-roles-im-applying-for-paste-a-5h0</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/komal_deep_355/redmargin-does-your-project-actually-match-the-job-4414" class="crayons-story__hidden-navigation-link"&gt;Redmargin: Does Your Project Actually Match the Job?&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/komal_deep_355/redmargin-does-your-project-actually-match-the-job-4414" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/komal_deep_355" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4047872%2F8ff91215-5a7e-4982-a0f0-d213cfb89ee7.png" alt="komal_deep_355 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/komal_deep_355" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Komal deep
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Komal deep
                
                
              
              &lt;div id="story-author-preview-content-4870601" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/komal_deep_355" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4047872%2F8ff91215-5a7e-4982-a0f0-d213cfb89ee7.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Komal deep&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/komal_deep_355/redmargin-does-your-project-actually-match-the-job-4414" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/komal_deep_355/redmargin-does-your-project-actually-match-the-job-4414" id="article-link-4870601"&gt;
          Redmargin: Does Your Project Actually Match the Job?
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hf26challenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hf26challenge&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/komal_deep_355/redmargin-does-your-project-actually-match-the-job-4414" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/komal_deep_355/redmargin-does-your-project-actually-match-the-job-4414#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>career</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Redmargin: Does Your Project Actually Match the Job?</title>
      <dc:creator>Komal deep</dc:creator>
      <pubDate>Sun, 18 Oct 2026 06:55:50 +0000</pubDate>
      <link>https://dev.to/komal_deep_355/redmargin-does-your-project-actually-match-the-job-4414</link>
      <guid>https://dev.to/komal_deep_355/redmargin-does-your-project-actually-match-the-job-4414</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Here is a problem I lived through as a fresh graduate.&lt;/p&gt;

&lt;p&gt;Hiring has changed. Freshers send dozens of applications, recruiters skim for seconds, and rejections come with no feedback. Every fresher I know is stuck on the same questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Which project should I put on my resume for this job?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Does this project actually match what the job asks for?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;If something is missing, what exactly should I learn, and in what order?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nobody answers these. Resume advice is vague: "add more projects," "make it stronger." So I built the tool I wished I had.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redmargin&lt;/strong&gt; is an honest reviewer for your GitHub projects. You give it a public repo and a target role. It compares your repo with what job postings ask for, shows where it found evidence of each skill and where it didn't, and gives you a ranked fix plan with a day-by-day timetable.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "How May I Help" Desk
&lt;/h3&gt;

&lt;p&gt;Not everyone has the same time. A student with two free hours a day and a job-seeker with eight need different plans. The &lt;strong&gt;How May I Help Desk&lt;/strong&gt; builds a personalized plan around the hours you can dedicate to a project, so the fixes and timetable fit your real schedule.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who would pay, and why?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Job seekers and freshers&lt;/strong&gt; pay to learn which projects fit which job, what is missing, and how to close the gap in the time they have.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Companies and recruiters&lt;/strong&gt; care about how closely a candidate's projects match the required skill set. That match percentage is something Redmargin could produce from the repo itself. Today the product serves candidates, and recruiter-facing evaluation is the direction I want to take it next.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Built to be trusted
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Numbers are deterministic.&lt;/strong&gt; Skill percentages, repo signals and fix hours are computed in code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The LLM only writes the critique,&lt;/strong&gt; and every claim must cite evidence that can be checked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unsupported claims are dropped and counted,&lt;/strong&gt; which gives a real "how often did the model invent things" number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing evidence is never treated as proof of a missing skill.&lt;/strong&gt; Skills that can't be inferred from source are marked "not assessed".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Live demo: &lt;a href="https://redmargin.onrender.com" rel="noopener noreferrer"&gt;https://redmargin.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/KomalDeep355" rel="noopener noreferrer"&gt;
        KomalDeep355
      &lt;/a&gt; / &lt;a href="https://github.com/KomalDeep355/Redmargin" rel="noopener noreferrer"&gt;
        Redmargin
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An evidence-first GitHub repository reviewer that turns skill gaps into actionable improvement plans.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Repo Reviewer (working name — rename freely)&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;An honest reviewer for your GitHub projects. You give it a repo and a target role; it compares the repo with what
real job postings ask for, shows where repository evidence was not found, and gives a ranked fix plan with a
day-by-day timetable. Missing repository evidence is not proof that a candidate lacks a skill.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Design rule:&lt;/strong&gt; numbers are deterministic (signals, percentages, hours). The LLM only writes the critique, and every
claim must cite evidence we can check. Unsupported claims are dropped and counted, which gives you a measurable
"how often did the model invent things" number for your write-up.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run it (Windows PowerShell)&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-powershell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;cd D:\Projects\repo&lt;span class="pl-k"&gt;-&lt;/span&gt;reviewer
py &lt;span class="pl-k"&gt;-&lt;/span&gt;m venv .venv
.venv\Scripts\Activate.ps1        &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; if blocked: Set-ExecutionPolicy -Scope Process Bypass&lt;/span&gt;
pip install &lt;span class="pl-k"&gt;-&lt;/span&gt;r requirements.txt
copy .env.example .env             &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; then edit .env&lt;/span&gt;
uvicorn app.main:app &lt;span class="pl-k"&gt;--&lt;/span&gt;reload&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open &lt;a href="http://127.0.0.1:8000" rel="nofollow noopener noreferrer"&gt;http://127.0.0.1:8000&lt;/a&gt;. Run tests…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/KomalDeep355/Redmargin" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; FastAPI backend, a plain HTML/CSS/JS frontend, pytest tests, and a Render blueprint for deployment.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fetch the repo&lt;/strong&gt; through the GitHub API (file tree plus key files, no cloning), with limits on tree size, file count and concurrency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detect signals deterministically:&lt;/strong&gt; tests, CI, Docker, README quality, committed &lt;code&gt;.env&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build the skill matrix.&lt;/strong&gt; A taxonomy in &lt;code&gt;skills.json&lt;/code&gt; defines how each skill appears in job postings and in a repo (dependencies, paths, keywords). The tool compares how many postings ask for a skill with the evidence found in the repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan the fixes.&lt;/strong&gt; Gaps become ranked fixes from an editable &lt;code&gt;fix_library.json&lt;/code&gt;, then a day-by-day timetable, adjusted by the How May I Help Desk to your available hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write the critique with open-source AI,&lt;/strong&gt; then validate every claim against readable repo files. Claims without evidence are dropped and counted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extras built on the same evidence:&lt;/strong&gt; repo-grounded interview questions, evidence-checked resume bullets, and a weighted hackathon-score estimate.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How It Works: The Pipeline
&lt;/h2&gt;

&lt;p&gt;Redmargin has two separate parts: a &lt;strong&gt;live review pipeline&lt;/strong&gt; that users run, and an &lt;strong&gt;offline machine-learning experiment&lt;/strong&gt; that is not yet connected to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LIVE REVIEW (what users run today)
──────────────────────────────────
 Public repo URL + target role + hours available
                    │
                    ▼
 1. FETCH       GitHub API, bounded file set, no clone, no execution
                    ▼
 2. SIGNALS     tests, CI, deploy files, README quality, hygiene issues
                    ▼
 3. SKILL MATRIX   skills.json taxonomy + repo markers
                    │   compared with real job postings
                    ▼
 4. PLANNER     ranked fixes (fix_library.json) → timetable sized to your hours
                    ▼
 5. LLM (optional)   open-source model writes the explanation
                    ▼
 6. VALIDATOR   unsupported claims are dropped and counted
                    ▼
        Ranked fix plan + day-by-day timetable


OFFLINE EXPERIMENT (not wired into live scoring yet)
────────────────────────────────────────────────────
 3 job-posting CSVs → join → dedupe → clean → role labels
        → split by company → TF-IDF + Logistic Regression
        → validation threshold → held-out test metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key idea: &lt;strong&gt;numbers come from code, words come from the model, and a validator checks the words against the repo.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Pipeline and Model Experiment
&lt;/h2&gt;

&lt;p&gt;I wanted Redmargin to be more than an LLM that looks at a GitHub repository and guesses whether it is good enough for a job. So I separated the system into a deterministic review pipeline and an offline machine-learning experiment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Collecting and preparing the data
&lt;/h3&gt;

&lt;p&gt;My local dataset workflow started with three source CSV files containing job-posting information. It joined the job, summary and skill tables on &lt;code&gt;job_link&lt;/code&gt;, removed duplicates, discarded records without usable titles or descriptions, normalized the text, and classified postings into target-role categories.&lt;/p&gt;

&lt;p&gt;The initial join produced &lt;strong&gt;12,217 postings, including 12,212 with source-provided skill labels&lt;/strong&gt;. After filtering and deduplication, &lt;strong&gt;1,223 postings&lt;/strong&gt; remained:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Postings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data Scientist&lt;/td&gt;
&lt;td&gt;729&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NLP/ML Engineer&lt;/td&gt;
&lt;td&gt;270&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Software Engineer&lt;/td&gt;
&lt;td&gt;188&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forward Deployed Engineer&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I split the records &lt;strong&gt;by company&lt;/strong&gt;, not by random row, so postings from the same company never appear in more than one split. That reduces leakage.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Split&lt;/th&gt;
&lt;th&gt;Postings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Training&lt;/td&gt;
&lt;td&gt;841&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation&lt;/td&gt;
&lt;td&gt;183&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test&lt;/td&gt;
&lt;td&gt;199&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The labels came from the source data and are not independently verified ground truth. I treat them as noisy labels, not definitive statements about what every employer requires.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data transparency:&lt;/strong&gt; the repository documents this workflow and these counts. The original dataset's source, collection date and redistribution licence still need confirmation before I can present it as a fully documented, redistributable corpus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Training the skill-extraction baseline
&lt;/h3&gt;

&lt;p&gt;For the offline experiment I built a multi-label skill-extraction baseline using &lt;strong&gt;TF-IDF vectorization and Logistic Regression&lt;/strong&gt;. TF-IDF turns job-description text into numerical features based on the words and phrases it contains, and Logistic Regression learns to predict skill labels from those features.&lt;/p&gt;

&lt;p&gt;I limited the experiment to the 50 most frequent training labels. The vectorizer and classifier were fitted on the training split only. I used the validation split to choose a global probability threshold of 0.20, then evaluated on the held-out test split.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Validation micro-F1&lt;/td&gt;
&lt;td&gt;0.5305&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test micro-F1&lt;/td&gt;
&lt;td&gt;0.5483&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test macro-F1&lt;/td&gt;
&lt;td&gt;0.3335&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are exploratory results, and they have clear limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The test data contained 3,670 skill-label occurrences &lt;strong&gt;outside&lt;/strong&gt; the training vocabulary, so the model does not cover the long tail of skills.&lt;/li&gt;
&lt;li&gt;The metrics cover only the selected 50-label vocabulary. They are &lt;strong&gt;not&lt;/strong&gt; overall skill-extraction performance and &lt;strong&gt;not&lt;/strong&gt; repository-review accuracy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fitted vectorizer, classifier, vocabulary, threshold and training metadata are saved locally as a model artifact. &lt;strong&gt;This experiment is not yet integrated into Redmargin's live scoring.&lt;/strong&gt; It is a starting point for future evaluation, not a claim that the app already uses a trained model to make hiring recommendations.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the live review works
&lt;/h3&gt;

&lt;p&gt;When a user submits a public GitHub repo and a target role, Redmargin fetches a bounded set of files through the GitHub API without cloning or executing the project. Then it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks repository signals such as &lt;strong&gt;tests, CI configuration, deployment files, README quality and hygiene issues&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Builds a skill-evidence matrix from the skill taxonomy and repository markers.&lt;/li&gt;
&lt;li&gt;Compares &lt;strong&gt;skill&lt;/strong&gt; mentions in available &lt;strong&gt;real job postings&lt;/strong&gt; with the evidence in the repo.&lt;/li&gt;
&lt;li&gt;Ranks fixes and builds a &lt;strong&gt;timetable based&lt;/strong&gt; on the &lt;strong&gt;user's available hours.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Optionally asks an &lt;strong&gt;OpenAI-compatible LLM&lt;/strong&gt; to write an explanation grounded in repository evidence.&lt;/li&gt;
&lt;li&gt;Validates the generated findings against the evidence and discards unsupported claims.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The numeric scoring and fix estimates are deterministic. Role priorities are human-configured, not learned from the training data. Synthetic sample postings are excluded from market percentages, and the app withholds market-based conclusions when there isn't enough real-posting evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  What comes next
&lt;/h3&gt;

&lt;p&gt;The next steps are to confirm the dataset's source and licence, review the noisy skill labels, expand coverage across roles, and build an independently human-labelled benchmark of repository signals. Then I'll compare the offline model against the deterministic baseline before it influences any live decision.&lt;/p&gt;

&lt;p&gt;The goal is not to replace evidence with a model prediction. It is to use machine learning where it adds value, while keeping every user-facing finding inspectable and honest about uncertainty.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Open-Source AI Layer
&lt;/h3&gt;

&lt;p&gt;Redmargin supports OpenAI-compatible endpoints, so it works with local inference through Ollama or a hosted open-weight model. A rule-based mock mode is also available, so anyone can explore the review workflow without configuring an API key.&lt;/p&gt;

&lt;p&gt;The project is designed to keep deterministic scoring separate from AI-generated explanations. The model never decides the numbers. It only explains them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Honest Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The included job postings are synthetic test data, not evidence of current hiring-market demand. They are excluded from market percentages.&lt;/li&gt;
&lt;li&gt;Real market comparisons require a sufficient collection of real job postings.&lt;/li&gt;
&lt;li&gt;Only public GitHub repositories are supported.&lt;/li&gt;
&lt;li&gt;Fix-hour estimates are editable defaults, not guaranteed predictions.&lt;/li&gt;
&lt;li&gt;The current product focuses on helping candidates improve their projects. Recruiter-facing evaluation is a potential future direction.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;A tool that judges people's work has to earn trust, and a closed black box can't do that.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy and cost for the people who need it most.&lt;/strong&gt; Freshers can't afford subscriptions, and their code and career plans are personal. With open-weight models and local inference, Redmargin can run on a candidate's own machine without that data leaving it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspectable, not magical.&lt;/strong&gt; The skill taxonomy is a plain JSON file. Adding a role, skill or fix is a single file edit, so anyone can extend Redmargin to a new job market without asking a vendor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measurable honesty.&lt;/strong&gt; Because I control the model layer, I can measure how often the critique makes unsupported claims. That trust metric is the heart of the project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Economics that work for a side hustle.&lt;/strong&gt; Per-call API pricing would eat the margin of a service aimed at students. Open models make a low-cost audit service possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My First User
&lt;/h2&gt;

&lt;p&gt;My first user was a friend, and I didn't charge her anything.&lt;/p&gt;

&lt;p&gt;She is applying for different kinds of jobs and internships. She was making projects for her resume but had no way to see where the skills were missing in them, or which projects matched which role. That is exactly the problem Redmargin was built for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built Redmargin
&lt;/h2&gt;

&lt;p&gt;I built Redmargin because I know how difficult it is to finish a project and still wonder whether it is good enough for the role you're applying for.&lt;/p&gt;

&lt;p&gt;I wanted to create something more useful than another score generator: a tool that shows its evidence, admits what it cannot determine, and gives you a practical next step.&lt;/p&gt;

&lt;p&gt;Redmargin is built around one principle: don't just tell developers what's wrong with their projects. Show them the evidence, then help them fix it.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>In Delhi NCR, "go touch grass" can be bad advice. So I built a tool that checks the air first.</title>
      <dc:creator>Komal deep</dc:creator>
      <pubDate>Mon, 12 Oct 2026 05:48:32 +0000</pubDate>
      <link>https://dev.to/komal_deep_355/in-delhi-ncr-go-touch-grass-can-be-bad-advice-so-i-built-a-tool-that-checks-the-air-first-355j</link>
      <guid>https://dev.to/komal_deep_355/in-delhi-ncr-go-touch-grass-can-be-bad-advice-so-i-built-a-tool-that-checks-the-air-first-355j</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Every &lt;strong&gt;October&lt;/strong&gt;, the advice around me in &lt;strong&gt;Delhi NCR&lt;/strong&gt; quietly changes. &lt;strong&gt;"Go for a walk"&lt;/strong&gt; becomes &lt;strong&gt;"check the AQI first." Diwali&lt;/strong&gt; is less than a month away, and the air is about to get worse.&lt;/p&gt;

&lt;p&gt;Checking the AQI is easy. Deciding what to do with the number is harder. Is this a good time for a walk? Would another hour be better? What if a &lt;strong&gt;child&lt;/strong&gt;, or someone with a &lt;strong&gt;lung condition&lt;/strong&gt;, is going out? And if I do go, where is the &lt;strong&gt;nearest park&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Roamwise checks the air before you go touch grass.&lt;/strong&gt; You say who is going and what you plan to do, and it answers in one line: &lt;strong&gt;Good to go&lt;/strong&gt;, &lt;strong&gt;Go easy&lt;/strong&gt; or &lt;strong&gt;Stay in&lt;/strong&gt;. Then it shows the cleanest daylight window in the next 24 hours and the nearest parks. It is for anyone in Delhi NCR (or any city) who wants to be outside and does not want to decode a chart first.&lt;/p&gt;

&lt;h3&gt;
  
  
  It gives you:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A verdict that changes with the person and the plan.&lt;/strong&gt; Choose "most adults" or "children, older adults, heart or lung conditions," and "a relaxed walk" or "running or hard exercise." The thresholds shift with each choice. A reading of 70 µg/m³ means &lt;strong&gt;Go easy&lt;/strong&gt; for most adults on an easy walk, but &lt;strong&gt;Stay in&lt;/strong&gt; for a child, an older adult or someone with a heart or lung condition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-Meteo's hourly PM2.5 forecast for the next 24 hours&lt;/strong&gt;, with the cleanest daylight window highlighted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A short explanation in English, Hindi or Hinglish.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parks within 3 km&lt;/strong&gt;, with walking time and a map, from OpenStreetMap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice input and an assistant, Ask Roamwise.&lt;/strong&gt; Voice input lets users speak their outdoor-planning questions instead of typing, while Ask Roamwise answers questions about air quality, weather, and suitable times to visit nearby parks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The challenge says the best builds make the screen the shortest part of the experience. So the page leads with one coloured banner and one sentence. The chart, parks and map are there if you want them, but the answer comes first, and then you go outside.&lt;/p&gt;

&lt;h3&gt;
  
  
  Taking it outside
&lt;/h3&gt;

&lt;p&gt;In &lt;strong&gt;Delhi NCR,&lt;/strong&gt; deciding when to go for a run isn't always as simple as checking the weather. Some mornings look foggy, evenings can feel hazy too, and by midday the sunlight makes the outdoors look more inviting. But a sunny sky doesn't necessarily mean the air is clean.&lt;/p&gt;

&lt;p&gt;That was one of the real-life problems behind &lt;strong&gt;Roamwise&lt;/strong&gt;. I wanted to make it easier to look beyond what the sky looks like and check the air-quality forecast before heading outside. The goal is to help people find a more suitable time for outdoor activities instead of relying on appearance alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  What my friend said
&lt;/h3&gt;

&lt;p&gt;I asked a friend who goes running every day to try &lt;strong&gt;Roamwise&lt;/strong&gt;. He often finds it difficult to decide when to run because the air can look foggy in the morning and evening, while the daylight and sunshine can make conditions seem better.&lt;/p&gt;

&lt;p&gt;The problem isn't just whether to go running; it's figuring out &lt;strong&gt;which time of day might be better for it&lt;/strong&gt;. Roamwise is my attempt to make that decision easier by combining hourly PM2.5 forecasts with activity-specific recommendations.&lt;/p&gt;

&lt;p&gt;What matters to me is that the app doesn't simply tell someone what they want to hear. It uses explicit rules to produce the verdict, while the AI explains it in plain language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/7UpKBja9AIA" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Search a city, check the air-quality verdict for different activity levels, explore the hourly PM2.5 forecast, and find nearby parks and a better window to head outdoors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://roamwise-4e26.onrender.com" rel="noopener noreferrer"&gt;roamwise-4e26.onrender.com&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/KomalDeep355" rel="noopener noreferrer"&gt;
        KomalDeep355
      &lt;/a&gt; / &lt;a href="https://github.com/KomalDeep355/Roamwise" rel="noopener noreferrer"&gt;
        Roamwise
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Roamwise 🌿&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Check the air before you go touch grass.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Roamwise helps you decide whether it is a good time to go outdoors by showing current air-quality conditions, the cleanest daylight window in the next 24 hours, and nearby parks.&lt;/p&gt;
&lt;p&gt;Built with Delhi NCR in mind, where "go outside" can be bad advice around Diwali, Roamwise is designed to work for other cities too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://roamwise-4e26.onrender.com" rel="nofollow noopener noreferrer"&gt;https://roamwise-4e26.onrender.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Write-up on DEV:&lt;/strong&gt; &lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The one design rule&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;strong&gt;go / go easy / stay in verdict is computed using fixed PM2.5 rules, not a language model.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An open-weight language model explains that verdict in plain language in English, Hinglish or Hindi. If the generated explanation contradicts the verdict or makes a medical claim, Roamwise replaces it with a template explanation.&lt;/p&gt;
&lt;p&gt;For a health-adjacent application, the language model explains the decision; it does not make the underlying safety decision.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Air-quality forecast:&lt;/strong&gt; Hour-by-hour PM2.5…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/KomalDeep355/Roamwise" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;A Python and FastAPI backend with a plain web frontend. It is open source and runs locally with Ollama.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Air quality and weather come from &lt;strong&gt;Open-Meteo&lt;/strong&gt;. Parks come from &lt;strong&gt;OpenStreetMap&lt;/strong&gt; through the Overpass API. A SQLite cache cuts repeated requests and serves the last good data if a source is down.&lt;/p&gt;

&lt;h3&gt;
  
  
  The rule I did not give to the model
&lt;/h3&gt;

&lt;p&gt;A language model does not write the verdict. It comes from fixed PM2.5 thresholds in plain Python, and the model only explains the result. This is a &lt;strong&gt;health-adjacent decision&lt;/strong&gt;, and a fluent explanation is not evidence that the recommendation is right.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# (go_max, caution_max) in µg/m³ of PM2.5
&lt;/span&gt;&lt;span class="n"&gt;THRESHOLDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;light&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vigorous&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sensitive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;light&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sensitive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vigorous&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&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;These are my application's practical heuristics, based on &lt;strong&gt;India's CPCB PM2.5 bands&lt;/strong&gt;. They are not official CPCB activity-specific recommendations and not medical limits. CPCB defines its bands for 24-hour averages, whereas Roamwise applies the thresholds to hourly forecast values. They live in one dictionary so anyone can read them, change them and argue with me.&lt;/p&gt;

&lt;p&gt;The model takes the verdict, best window, and nearest park as fixed facts and writes a few plain sentences. If its text contradicts the verdict or makes a medical claim, the app drops it and shows standard wording instead, and says which one you are reading. I tested the core decision logic against different activity levels and user sensitivity settings. The verdict is rule-based, so the AI explanation cannot override a &lt;strong&gt;“Stay In”&lt;/strong&gt; recommendation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open-Meteo (air quality + weather) ─┐
OpenStreetMap / Overpass (parks) ───┤
                                    ▼
                  SQLite cache (stale fallback)
                                    ▼
        Fixed rules: PM2.5 thresholds, daylight, rain, heat
              ▼ verdict + windows + ranked parks
        Open-weight LLM explains it (template fallback)
                                    ▼
             FastAPI  ──►  page with chart, parks and map
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Where the forecast comes from
&lt;/h3&gt;

&lt;p&gt;Roamwise uses Open-Meteo's hourly &lt;strong&gt;PM2.5 air-quality forecast&lt;/strong&gt; for the next 24 hours. It does not currently use a separately trained forecasting model. Its own contribution is the decision layer: applying configurable thresholds, considering relevant weather conditions, and identifying potentially better outdoor windows from the forecast data.&lt;/p&gt;

&lt;h3&gt;
  
  
  The open models and deployment
&lt;/h3&gt;

&lt;p&gt;Locally, Roamwise uses &lt;strong&gt;Gemma 3 4B&lt;/strong&gt; through Ollama. The deployed version is configured to use &lt;strong&gt;&lt;code&gt;gemma3:4b&lt;/code&gt;&lt;/strong&gt; through an OpenAI-compatible endpoint. In both configurations, the model explains the rule-based verdict rather than deciding it. The app is deployed as a FastAPI service on Render using &lt;code&gt;render.yaml&lt;/code&gt;, with a health-check endpoint and environment variables for the model connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  What went wrong
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The forecast-versus-verdict distinction needed care.&lt;/strong&gt; I had to make sure Roamwise clearly separates the hourly PM2.5 forecast from the rule-based activity recommendation, rather than implying that I had trained a separate forecasting model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The AI needed boundaries.&lt;/strong&gt; I wanted the assistant to explain the recommendation, not invent a more reassuring answer when the rules say “Stay In.” Keeping the verdict rule-based helps maintain that separation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Local and deployed configurations weren't identical.&lt;/strong&gt; Locally, I use Gemma 3 4B through Ollama, while the Render deployment uses an OpenAI-compatible endpoint. That meant I needed to verify the deployed model and provider separately instead of assuming the local setup carried over.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I had to be careful about what I claimed.&lt;/strong&gt; It would be easy to describe the activity thresholds as official health guidance or imply that every feature had been thoroughly tested. I kept the distinction between practical heuristics and official guidance explicit and avoided claiming unverified test results.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What it can't do
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Air quality is a regional model estimate, not a reading at a particular park.&lt;/li&gt;
&lt;li&gt;The thresholds are heuristics, not medical limits.&lt;/li&gt;
&lt;li&gt;A park is not automatically cleaner than the street outside it. Parks are ranked by distance only, which does not show air quality.&lt;/li&gt;
&lt;li&gt;Forecasts can be wrong or change.&lt;/li&gt;
&lt;li&gt;This is a planning aid, not medical advice or a replacement for official alerts. If you have a heart or lung condition, follow your doctor's advice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. I can swap the model.&lt;/strong&gt; The explanation layer works with local Ollama or a hosted endpoint, so I can try different open models without touching the part that decides the verdict.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Local mode keeps more on my machine, but not everything.&lt;/strong&gt; When I run Gemma locally, the choices about who is going (for example, a child or someone with a lung condition) and the explanation request are processed locally. The place searched still goes to Open-Meteo and OpenStreetMap to fetch data. On the hosted demo, the explanation request goes to the configured OpenAI-compatible API provider and is subject to that provider’s data-handling practices. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The rules are inspectable.&lt;/strong&gt; The thresholds are Python code, not instructions hidden in a prompt. Anyone can read them, test them and change them. A closed API would have made that decision invisible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Small and open has a cost.&lt;/strong&gt; I haven't published a reliable benchmark comparing local &lt;strong&gt;Gemma 3 4B&lt;/strong&gt; with the hosted model, so I won't pretend I have one. Models can differ in response speed and the quality of their explanations. That's why the verdict never depends on the model: the rules determine the recommendation, and the model explains it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 3 4B, run locally through Ollama, generates plain-language explanations in English, Hindi, or Hinglish from fixed facts. It explains the rule-based verdict; it never decides whether going outdoors is recommended. I haven't recorded a reliable latency benchmark yet, so I won't claim a speed advantage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best Use of Render&lt;/strong&gt;: One FastAPI web service deployed using render.yaml, with a health-check endpoint and environment variables for the hosted model connection. This keeps deployment configuration separate from the application code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best Use of ElevenLabs:&lt;/strong&gt; Integrated ElevenLabs into Roamwise’s AI agent to support a more natural, voice-enabled interaction. This lets users interact with the assistant beyond traditional text-based input.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you live in Delhi NCR, try Roamwise this week and tell me what is missing. Which city should I test next? Next, I want to rank places by more than distance, for example how close a park is to a major road.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>FriendOS: I Built an AI That Tells My Panicking Friend What to Do Next</title>
      <dc:creator>Komal deep</dc:creator>
      <pubDate>Mon, 05 Oct 2026 06:54:12 +0000</pubDate>
      <link>https://dev.to/komal_deep_355/friendos-i-built-an-ai-that-tells-my-panicking-friend-what-to-do-next-59af</link>
      <guid>https://dev.to/komal_deep_355/friendos-i-built-an-ai-that-tells-my-panicking-friend-what-to-do-next-59af</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;FriendOS&lt;/strong&gt;, a little AI thinking partner that answers one question: &lt;strong&gt;"What should I do next?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I built it for a friend who is always one deadline away from panic.&lt;/p&gt;

&lt;p&gt;She isn't lazy, and she isn't unorganized in the way people usually mean it. Her problem is that everything arrives at once. An assignment is due today. A message needs a reply. Something needs buying. Her room needs cleaning. And all of it is in her head at the same time, loud and tangled.&lt;/p&gt;

&lt;p&gt;When that happens, she freezes. Not because she doesn't know what the tasks are, but because she doesn't know &lt;strong&gt;which one to start&lt;/strong&gt;, or &lt;strong&gt;where to start&lt;/strong&gt; with it.&lt;/p&gt;

&lt;p&gt;Then comes the second problem. To actually do anything, she bounces between five or six places at once: a search tab for one thing, her email for another, a shopping site, a notes app, an AI chat, a study page. Nothing is connected. Every task needs its own hunt for the right resource, and by the time she's found it, she's lost the thread and the panic is worse than before.&lt;/p&gt;

&lt;p&gt;A normal to-do app didn't help her. It gave her &lt;strong&gt;more information&lt;/strong&gt; when she needed &lt;strong&gt;less thinking&lt;/strong&gt;. So I built something different.&lt;/p&gt;

&lt;p&gt;FriendOS skips the forms. You brain-dump everything, by typing or by speaking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I need to submit my assignment today, finish two sections, clean my room this weekend, and buy a charger because mine is damaged."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;FriendOS turns that into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔥 &lt;strong&gt;DO NOW&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🌼 &lt;strong&gt;DO SOON&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🌿 &lt;strong&gt;CAN WAIT&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;⏱️ &lt;strong&gt;NEXT 30 MINUTES&lt;/strong&gt;: one to three small, realistic steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then it helps her actually &lt;strong&gt;start&lt;/strong&gt;, so she doesn't have to go hunting. Each task comes with a first move: a shopping task opens a search, an email task builds a prompt and opens Gmail, a study or writing task generates a breakdown prompt and opens an AI assistant. Instead of juggling tabs, she gets one place that says &lt;em&gt;do this, start here&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The most important rule is that FriendOS &lt;strong&gt;only works with what you actually tell it&lt;/strong&gt;. My first version kept adding things like "research potential topics for future assignments" that nobody asked for. For someone who is already overwhelmed, a planner that invents more work is the last thing she needs. So FriendOS never invents tasks or deadlines.&lt;/p&gt;

&lt;p&gt;It works in English, Hindi, Punjabi, and Hinglish, because my friend doesn't think in only one language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://friendos-rizl.onrender.com/" rel="noopener noreferrer"&gt;https://friendos-rizl.onrender.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try this brain dump:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I need to submit my assignment to my professor today. I still need to finish two sections. I also need to clean my room this weekend. I want to buy a new charger because mine is damaged.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/KomalDeep355" rel="noopener noreferrer"&gt;
        KomalDeep355
      &lt;/a&gt; / &lt;a href="https://github.com/KomalDeep355/FriendOS" rel="noopener noreferrer"&gt;
        FriendOS
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;FriendOS 🌼&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Your little thinking partner. It answers one question: "What should I do next?"&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;FriendOS turns a messy brain dump into a calm, realistic priority plan, then tells you exactly what small step to take in the next 30 minutes. It only works with what you actually tell it. It does not invent tasks or deadlines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo:&lt;/strong&gt; &lt;a href="https://friendos-rizl.onrender.com/" rel="nofollow noopener noreferrer"&gt;https://friendos-rizl.onrender.com/&lt;/a&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/KomalDeep355/FriendOS" rel="noopener noreferrer"&gt;https://github.com/KomalDeep355/FriendOS&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why I built it&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;I built FriendOS for a friend who always has too many things to keep track of: assignments, errands, things to buy, things to clean. They didn't need another productivity dashboard. They already knew what was on their plate. The hard part was deciding &lt;strong&gt;which thing to do right now&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The problem isn't a lack of organization. It's decision overload.&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;Too many things in my head
        ↓
Brain dump instead of forms
        ↓
AI prioritization instead of manual sorting
        ↓
NEXT 30 MINUTES instead of&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/KomalDeep355/FriendOS" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text or microphone
      ↓
FastAPI  →  ElevenLabs Scribe v2 (voice only)
      ↓
FastAPI  /api/organize
      ↓
Open-weight Llama
      ↓
Strict JSON → validation → plan
      ↓
DO NOW / DO SOON / CAN WAIT / NEXT 30 → contextual action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; plain HTML, CSS, and JavaScript, with a calm, cozy, Pinterest-style board look. Task completion is saved in &lt;code&gt;localStorage&lt;/code&gt;, so there's no database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Python and FastAPI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI:&lt;/strong&gt; an open-weight &lt;strong&gt;Llama&lt;/strong&gt; model, called through an OpenAI-compatible chat endpoint. Locally I ran it with &lt;strong&gt;Ollama&lt;/strong&gt; (&lt;code&gt;llama3.2:3b&lt;/code&gt;). For the public demo I point the same code at a hosted Llama endpoint, so anyone can try it without installing anything. Switching is two environment variables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice:&lt;/strong&gt; the browser records audio with &lt;code&gt;MediaRecorder&lt;/code&gt;, FastAPI sends it to &lt;strong&gt;ElevenLabs Scribe v2&lt;/strong&gt;, and the transcript goes straight into the planning pipeline. This is voice &lt;strong&gt;input&lt;/strong&gt; only. FriendOS doesn't speak back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting:&lt;/strong&gt; Render.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Making a small model behave.&lt;/strong&gt; The system prompt tells the model never to invent tasks, deadlines, or events, never to duplicate tasks, not to force tasks into categories, and to keep NEXT 30 realistic. I don't trust the prompt alone, though. A normalization layer in the backend strips markdown fences, fills missing fields, removes empty and duplicate tasks, and drops any NEXT 30 item that points to a task that doesn't exist. That combination is what stopped the made-up tasks.&lt;/p&gt;

&lt;p&gt;I deliberately left out auth, a database, React, RAG, and multi-agent setups. I wanted something small enough to actually finish and put in front of my friend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;FriendOS is a very personal tool. People paste their half-formed worries and deadlines into it. An open-weight model made it possible to build around that.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Control over the experience.&lt;/strong&gt; Because I could shape the model's behavior through prompting and then validate its output, I could fix the exact problem my friend would have noticed: the planner inventing work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy is a real option.&lt;/strong&gt; With Ollama, the whole intelligence layer can run on your own machine, so your brain dump doesn't have to go to anyone's server. (The public demo uses a hosted Llama endpoint for convenience, and voice uses ElevenLabs, so I'm not claiming the live demo is fully local.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lock-in, and cost stays low.&lt;/strong&gt; Because the model sits behind a standard API shape, I could develop for free locally and then switch to a hosted open-weight endpoint for deployment without rewriting the app. A solo developer building for one friend shouldn't need a paid proprietary API to get started.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of ElevenLabs.&lt;/strong&gt; FriendOS takes voice input: you tap the mic and speak your brain dump, and &lt;strong&gt;ElevenLabs Scribe v2&lt;/strong&gt; transcribes it. The transcript goes straight into the Llama planning pipeline, so the plan appears without typing. This fits the category's "transcribe audio for a language model" use. FriendOS uses voice input only, not voice output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Render.&lt;/strong&gt; The FastAPI backend and the frontend are deployed on Render, which serves the live demo.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Connecting My Agent to SigNoz's MCP Server: A First-Timer's Debugging Diary</title>
      <dc:creator>Komal deep</dc:creator>
      <pubDate>Sun, 26 Jul 2026 15:35:24 +0000</pubDate>
      <link>https://dev.to/komal_deep_355/connecting-my-agent-to-signozs-mcp-server-a-first-timers-debugging-diary-4bmh</link>
      <guid>https://dev.to/komal_deep_355/connecting-my-agent-to-signozs-mcp-server-a-first-timers-debugging-diary-4bmh</guid>
      <description>&lt;p&gt;I want to start with the number that actually matters: &lt;strong&gt;three hours&lt;/strong&gt;. That's roughly how long I spent just getting a connection to succeed, out of a five-day hackathon window. If you're building something similar and hit the same walls, this post should save you most of that time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I was building, and why this angle
&lt;/h2&gt;

&lt;p&gt;This was my first hackathon. My background is data science and NLP — classification, prediction models, the kind of ML work where inputs and outputs are known quantities. Agents, MCP servers, observability — none of that was in my vocabulary two weeks ago. So this post is written from that specific angle: &lt;strong&gt;what actually trips someone up coming from ML into agent-plus-observability tooling for the first time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Agents of SigNoz (WeMakeDevs x SigNoz)&lt;/strong&gt;, I built an agent that connects to a self-hosted SigNoz instance through its MCP server, answers natural-language questions grounded in live telemetry, and then classifies what it finds — latency, error-rate, resource-exhaustion, dependency-failure, or healthy — with a confidence score traced back into SigNoz itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up SigNoz via Foundry
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
&lt;span class="sb"&gt;`&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://signoz.io/foundry.sh | bash&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;yaml&lt;/span&gt;
&lt;span class="na"&gt;`apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Installation&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;signoz&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;flavor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;compose&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;
  &lt;span class="na"&gt;mcp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="s"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
&lt;span class="sb"&gt;`&lt;/span&gt;foundryctl cast &lt;span class="nt"&gt;-f&lt;/span&gt; casting.yaml&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This brought up SigNoz and its bundled MCP server as separate Docker containers — a detail that turned out to matter a lot more than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #1: 401 Unauthorized
&lt;/h2&gt;

&lt;p&gt;My first connection attempt to &lt;strong&gt;&lt;a href="http://localhost:8000/mcp" rel="noopener noreferrer"&gt;http://localhost:8000/mcp&lt;/a&gt;&lt;/strong&gt; returned a flat &lt;strong&gt;401&lt;/strong&gt;. Self-hosted SigNoz's MCP server needs two specific headers, not the generic Bearer-token pattern I tried first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;streamablehttp_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;MCP_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SIGNOZ-API-KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SIGNOZ_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-SigNoz-URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://signoz-signoz-0:8080&lt;/span&gt;&lt;span class="sh"&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="nf"&gt;as &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bug #2: 400 Bad Request — the Docker networking trap
&lt;/h2&gt;

&lt;p&gt;Adding the headers got me past the** 401*&lt;em&gt;, straight into a **400&lt;/em&gt;*. I initially set X-SigNoz-URL to &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;, since that's where SigNoz's UI loaded from my own machine. But the MCP server runs inside its own container — and inside that container, localhost refers to the container itself, not my SigNoz instance. The server's own logs said it plainly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`"msg":"Invalid X-SigNoz-URL header" ... "error":"host \"localhost\" is not allowed"`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;docker ps showed the actual container name — signoz-signoz-0 —, and that's what needed to go in the header instead. This is the single biggest lesson I'd hand to my past self: when a container-to-container request fails, check what hostname the containers actually use to reach each other on the Docker network. It's rarely localhost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #3: a plain variable-naming mistake
&lt;/h2&gt;

&lt;p&gt;Once I fixed the headers, I hit a &lt;strong&gt;NameError:&lt;/strong&gt; name &lt;strong&gt;'read_stream'&lt;/strong&gt; &lt;strong&gt;is not defined&lt;/strong&gt;. I'd renamed the tuple unpacking on the async with line but hadn't updated where those names were used two lines later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;as &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;read_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;write_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Small, but the traceback pointed into the MCP library's internals, not my own line — a good reminder that an error surfacing deep in someone else's stack trace can still be your own typo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #4: 403 — a permissions gap, not a connection problem
&lt;/h2&gt;

&lt;p&gt;With the connection finally working, calling signoz_list_services returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`403: only viewers/editors/admins can access this resource`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API key itself was valid — the service account behind it just had no role assigned. The fix was in Settings → Service Accounts, assigning Editor or Admin. &lt;em&gt;Worth remembering: a valid key and a valid role are two separate things.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually built on top: reasoning, not just retrieval
&lt;/h2&gt;

&lt;p&gt;SigNoz's MCP server gives you raw retrieval — service lists, traces, metrics, logs. It has no opinion about what any of that data means. Coming from a classification background, my instinct was to add exactly that missing layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;classify_incident&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;telemetry_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;classification_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are classifying an observability finding.

User question: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Telemetry data retrieved: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;telemetry_data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;`
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Classify this into exactly ONE category: latency, error-rate, resource-exhaustion, dependency-failure, healthy, or unknown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Respond with ONLY valid JSON, nothing else:&lt;/strong&gt;&lt;br&gt;
`{{"category": "", "confidence": , "reasoning": ""}}"""&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;response = hf_client.chat_completion(
    model=MODEL_ID,
    messages=[{"role": "user", "content": classification_prompt}],
    max_tokens=150,
    temperature=0,
)
raw = response.choices[0].message.content.strip()
return json.loads(raw)`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The part I actually care about is where the result goes — not just into the chat response, but into SigNoz's own trace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;incident_classification&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;classify_span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;classification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classify_incident&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;telemetry_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;classify_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;incident.category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classification&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;classify_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;incident.confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classification&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means my agent's own judgment becomes something you can trace inside SigNoz, the same way you'd trace any other part of the system. SigNoz observes the target service; this makes it also observe the agent's reasoning about that service.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwbefsa0fhejkohk6fz64.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwbefsa0fhejkohk6fz64.png" alt="Screenshot: SigNoz trace view showing the incident_classification span with incident.category and incident.confidence attributes" width="799" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'd tell someone starting this from an ML background
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Docker networking will very likely be your biggest time sink — not the AI part. Budget for it honestly.&lt;/li&gt;
&lt;li&gt;Read the server's logs the moment a request fails for an unclear reason — docker logs_ _ told me more in thirty seconds than an hour of guessing at headers did.&lt;/li&gt;
&lt;li&gt;A service account's API key and its assigned role are two separate failure points — check both before assuming the key is broken.&lt;/li&gt;
&lt;li&gt;Look for where your actual background transfers, instead of trying to become a full infra person overnight. For me that was "don't just retrieve data, judge it" — that's ML thinking applied to an observability tool, and it's the one piece of this that's genuinely mine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;With more time, I'd add a small retrieval layer surfacing similar past incidents, and wire a SigNoz alert to trigger the agent automatically instead of waiting on a question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you're self-hosting SigNoz's MCP server and hit a &lt;strong&gt;401, 400, or 403&lt;/strong&gt; in that order — check your headers, your Docker hostnames, and your service account's role. That covered nearly everything that stood between a working agent and me, and I hope it saves you the three hours it cost me.&lt;/p&gt;

</description>
      <category>signoz</category>
      <category>wemakedev</category>
      <category>observability</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
