<?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: Prodini Admin</title>
    <description>The latest articles on DEV Community by Prodini Admin (@prodini_admin).</description>
    <link>https://dev.to/prodini_admin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3797646%2F6c0f4c52-4daa-4efd-8b72-637b74bb8dc3.png</url>
      <title>DEV Community: Prodini Admin</title>
      <link>https://dev.to/prodini_admin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prodini_admin"/>
    <language>en</language>
    <item>
      <title>How I Built a Self-Healing Node.js System That Fixes Production Bugs While I Sleep</title>
      <dc:creator>Prodini Admin</dc:creator>
      <pubDate>Sun, 08 Mar 2026 08:25:22 +0000</pubDate>
      <link>https://dev.to/prodini_admin/how-i-built-a-self-healing-nodejs-system-that-fixes-production-bugs-while-i-sleep-1bm9</link>
      <guid>https://dev.to/prodini_admin/how-i-built-a-self-healing-nodejs-system-that-fixes-production-bugs-while-i-sleep-1bm9</guid>
      <description>&lt;p&gt;So I had this problem. I run a couple of Node.js services and every few days something would break in production — a bad query, a null reference, some edge case nobody thought of. I'd find out from logs way too late, ssh in, figure out what happened, write a fix, test it, push it. Every. Time.&lt;/p&gt;

&lt;p&gt;At some point I thought — what if the system could just... fix itself? Or at least get 90% of the way there and ask me to approve?&lt;/p&gt;

&lt;p&gt;Thats how &lt;strong&gt;LevAutoFix&lt;/strong&gt; was born. Its an automated error detection and remediation system that watches production logs, classifies errors by severity, launches Claude Code in headless mode to generate fixes, and sends me a Telegram message to approve or skip. One tap from my phone, PR gets created.&lt;/p&gt;

&lt;p&gt;Heres how the whole thing works.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture — Two Processes
&lt;/h2&gt;

&lt;p&gt;The system is split into two separate processes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watcher&lt;/strong&gt; — runs on the production server. Its only job is watching log files, detecting errors, and relaying them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixer&lt;/strong&gt; — runs on my dev machine. Receives classified errors, manages the fix queue, runs Claude, handles Telegram interactions.&lt;/p&gt;

&lt;p&gt;This separation is intentional. I dont want anything doing code generation or git operations anywhere near production. The watcher is lightweight and read-only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┐          ┌─────────────────────┐
│  PROD SERVER     │          │  DEV MACHINE         │
│                  │          │                       │
│  Log Watcher     │───relay──│  Fix Queue            │
│  Error Classifier│          │  Claude Code Headless │
│                  │          │  Telegram Bot         │
│  (read-only)     │          │  Git Worktrees        │
└─────────────────┘          └─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1: Error Detection &amp;amp; Fingerprinting
&lt;/h2&gt;

&lt;p&gt;The watcher tails log files using a simple file watcher. When it spots an error, it generates a fingerprint — a hash of the error message + first 3 stack trace lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateFingerprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stripTimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stackLines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&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;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&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;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stripTimestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cleanMessage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stackLines&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&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;The fingerprint lets us group duplicate errors. If mongo goes down, you dont want 200 separate error events — you want one event that says "this happened 200 times in the last minute."&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: The Settle Window
&lt;/h2&gt;

&lt;p&gt;This was one of the most important design decisions. When an error comes in, we dont act on it immediately. We wait &lt;strong&gt;30 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why? Because errors come in cascades. One mongo timeout triggers 15 failed queries which trigger 30 API errors. If you act on the first one, you're fixing a symptom. Wait 30 seconds, group everything by fingerprint, and you see the real picture.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;settleTimers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;settledEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ErrorEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;settledEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;settledEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;settling&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;settledEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classifySeverity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;settledEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;settledEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;occurrenceCount&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;meetsMinSeverity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;settledEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;settledEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;detected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;settledEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;onSettleCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;settledEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settleDelayMs&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;Each new occurrence of the same error &lt;strong&gt;resets the timer&lt;/strong&gt;. So if errors keep coming, we keep waiting. Only when things calm down do we classify and decide what to do.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Severity Classification
&lt;/h2&gt;

&lt;p&gt;The classifier is intentionally simple. No ML, no fancy scoring — just regex patterns and occurrence counts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CRITICAL_PATTERNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="sr"&gt;/mongo.*&lt;/span&gt;&lt;span class="se"&gt;(?:&lt;/span&gt;&lt;span class="sr"&gt;error|fail|refused|timeout&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sr"&gt;/ECONNREFUSED/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sr"&gt;/jwt.*&lt;/span&gt;&lt;span class="se"&gt;(?:&lt;/span&gt;&lt;span class="sr"&gt;secret|undefined|invalid&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sr"&gt;/database.*&lt;/span&gt;&lt;span class="se"&gt;(?:&lt;/span&gt;&lt;span class="sr"&gt;down|unavailable|timeout&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;classifySeverity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;occurrenceCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ErrorSeverity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pattern&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;CRITICAL_PATTERNS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;critical&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;occurrenceCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;occurrenceCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;medium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;low&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Infrastructure errors (db down, auth broken) are always critical. Everything else escalates by count. Below medium gets ignored — if an error happened once, its probably not worth an automated fix.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; the watcher classifies, the fixer trusts. When the watcher relays an error to the fixer, it sends the severity and occurrence count along with it. The fixer doesnt re-classify. This avoids a nasty bug where occurrences would need to accumulate twice.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 4: Git Worktree Isolation
&lt;/h2&gt;

&lt;p&gt;This is the part that makes the whole thing safe. When a fix is approved, the system creates a &lt;strong&gt;git worktree&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/repo/.worktrees/hotfix/auto-&amp;lt;fingerprint&amp;gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A worktree is a full working copy of the repo on a separate branch. Claude can read, write, edit, run tests — whatever it needs. If the fix is wrong, you delete the worktree and nothing happened. Main branch is never touched.&lt;/p&gt;

&lt;p&gt;Early versions of this didn't have worktree isolation. Claude was committing to main at 3am. I woke up to some interesting git histories. Never again.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Claude Code Headless
&lt;/h2&gt;

&lt;p&gt;Heres where the AI comes in. The fixer launches Claude Code CLI in headless mode with a scoped prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fix this production error in the codebase.
Error: MongoServerError: connection pool closed
Stack: at MongoClient.connect (mongo-client.ts:88)
Path: POST /api/products/list
Severity: CRITICAL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude gets access to the full repo through the worktree and a set of tools — Read, Write, Edit, Glob, Grep, Bash. It explores the codebase, traces the error, and writes a fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The scoping is crucial.&lt;/strong&gt; Early on I tried just saying "fix my app" and the quality was terrible. Giving Claude the exact error, stack trace, and affected endpoint makes a huge difference. It knows exactly where to start looking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Honest results so far:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Critical infra errors&lt;/strong&gt; (db connections, auth) — claude fixes like 70-80% correctly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logic bugs&lt;/strong&gt; with clear stack traces — pretty solid&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vague errors&lt;/strong&gt; without good stacks — hit or miss&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 6: Telegram Approval
&lt;/h2&gt;

&lt;p&gt;Every fix goes through human approval. The bot sends a message with the error details and two buttons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚨 New Error — Approve Fix?

🔴 Severity: CRITICAL
Service: PA
Error: MongoServerError: connection pool closed
Path: POST /api/products/list

[✅ Approve Fix]  [❌ Skip]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tap Approve from my phone, the system creates a PR from the worktree branch. Skip and it gets shelved.&lt;/p&gt;

&lt;p&gt;I also built an &lt;strong&gt;interactive dashboard&lt;/strong&gt; so I can check the overall system status without typing commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🏠 LevAutoFix Dashboard

[📋 Queue Status]  [🚨 Recent Errors]
[📊 System Status] [🔄 Refresh]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each button edits the same message in-place with the requested view. No chat flooding. The errors view looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔴 [PA] MongoServerError: connection pool closed...
   🔧 fixing • 5m ago

🟠 [PA] jwt secret undefined - authentication broken...
   ⏳ detected • 12m ago

🟡 [GA] Cannot read property tenantId of undefined
   ✅ fixed • 2h ago
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;Nothing exotic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Typescript + Express&lt;/strong&gt; — API server for both watcher and fixer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB&lt;/strong&gt; — error events, fix queue, metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;node-telegram-bot-api&lt;/strong&gt; — bot with inline keyboards and callback handlers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code CLI&lt;/strong&gt; — headless mode for automated code generation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git worktrees&lt;/strong&gt; — isolation for each fix attempt&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;&lt;strong&gt;The settle window is everything.&lt;/strong&gt; Without it, cascade failures generate dozens of duplicate fix attempts. 30 seconds of patience saves hours of cleanup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope the AI prompt tightly.&lt;/strong&gt; Don't say "fix my app." Give it the exact error, the exact stack, the exact endpoint. The difference in fix quality is night and day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isolate with worktrees.&lt;/strong&gt; Let the AI experiment freely in a sandbox. If it works, merge it. If it doesn't, delete it. Zero risk to your main branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the watcher dumb.&lt;/strong&gt; The production component should be as simple as possible. Tail logs, classify, relay. All the complex stuff happens on the dev machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human in the loop matters.&lt;/strong&gt; Auto-fixing without approval sounds cool until Claude "fixes" your auth middleware at 3am. The Telegram approval step takes 2 seconds and has saved me multiple times.&lt;/p&gt;




&lt;h2&gt;
  
  
  Whats Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Auto-merge for low-risk fixes (small diff + tests pass + no sensitive files touched)&lt;/li&gt;
&lt;li&gt;Web dashboard for fix history analytics&lt;/li&gt;
&lt;li&gt;Open sourcing the watcher component — its generic enough to work with any Node.js app&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you're interested in trying something similar, the core idea is surprisingly simple: tail logs → fingerprint → settle → classify → worktree → AI → approve. Each piece is straightforward on its own. The magic is in how they connect.&lt;/p&gt;

&lt;p&gt;The whole thing is free and open source — just needs a Claude subscription. Planning to put the repo on GitHub soon.&lt;/p&gt;

&lt;p&gt;Happy to answer questions about any part of the setup.&lt;/p&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>How We Built an AI Product Manager That Actually Learns Your Team's Templates</title>
      <dc:creator>Prodini Admin</dc:creator>
      <pubDate>Sat, 28 Feb 2026 05:40:06 +0000</pubDate>
      <link>https://dev.to/prodini_admin/how-we-built-an-ai-product-manager-that-actually-learns-your-teams-templates-58k3</link>
      <guid>https://dev.to/prodini_admin/how-we-built-an-ai-product-manager-that-actually-learns-your-teams-templates-58k3</guid>
      <description>&lt;h1&gt;
  
  
  Writing PRDs shouldn't feel like punishment.
&lt;/h1&gt;

&lt;p&gt;If you're a product manager, you know the drill: spend 4 hours writing a PRD, share it with the team, get told "that's not our format," then spend another hour reformatting.&lt;/p&gt;

&lt;p&gt;Generic AI tools make it worse — they produce outputs that sound good but completely miss your team's conventions, terminology, and documentation standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Generic AI for Product Management
&lt;/h2&gt;

&lt;p&gt;We tried every AI assistant on the market. The results were consistently the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generic structure that doesn't match our templates
&lt;/li&gt;
&lt;li&gt;Hallucinated edge cases that waste engineering time
&lt;/li&gt;
&lt;li&gt;No awareness of past decisions or product context
&lt;/li&gt;
&lt;li&gt;Constant re-explaining of how our team works
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our Approach: RAG + Integration-First Architecture
&lt;/h2&gt;

&lt;p&gt;We built &lt;strong&gt;Prodini&lt;/strong&gt; with a fundamentally different approach. Instead of prompt engineering, we use Retrieval-Augmented Generation (RAG) to ingest your actual documentation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connect your tools&lt;/strong&gt; — Jira, Confluence, Figma, GitHub
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn your templates&lt;/strong&gt; — Prodini analyzes your existing PRDs, guidelines, and writing style
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate in context&lt;/strong&gt; — Every output is grounded in YOUR documentation
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result? PRDs that match your team's format from the first draft. No reformatting. No re-explaining.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edge Case Detection — My Favorite Feature
&lt;/h2&gt;

&lt;p&gt;Here's what keeps me up at night as a builder: edge cases that slip through planning and explode in production.&lt;/p&gt;

&lt;p&gt;Prodini analyzes your requirements and automatically flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing user flow scenarios
&lt;/li&gt;
&lt;li&gt;Potential conflicts with existing features
&lt;/li&gt;
&lt;li&gt;Error states nobody thought about
&lt;/li&gt;
&lt;li&gt;Permission and access control gaps
&lt;/li&gt;
&lt;li&gt;Integration edge cases between connected systems
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our testing, it consistently catches issues that even senior PMs with 10+ years of experience miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Stack
&lt;/h2&gt;

&lt;p&gt;For the curious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAG Pipeline&lt;/strong&gt; — Ingests and indexes Jira tickets, Confluence pages, Figma designs, and GitHub repos per tenant
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM Layer&lt;/strong&gt; — Claude AI for generation with context-aware prompting
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Integration&lt;/strong&gt; — Model Context Protocol for real-time Jira bi-directional sync
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSE Streaming&lt;/strong&gt; — Real-time agentic chat with file attachment support
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenant&lt;/strong&gt; — Complete data isolation per organization
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;After rolling this out to 700+ product managers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;16x faster&lt;/strong&gt; PRD creation (15 min vs 4+ hours)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;94% edge case coverage&lt;/strong&gt; detected automatically
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero reformatting&lt;/strong&gt; — matches your template from the first draft
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Q&amp;amp;A&lt;/strong&gt; — "What changed last sprint?" answered in under 5 seconds
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;We recently shipped &lt;strong&gt;Agentic Chat&lt;/strong&gt; — an autonomous AI mode where Prodini doesn't just answer questions, it takes actions. Upload a file, ask it to analyze your competitor's PRD, or have it review your sprint plan for gaps.&lt;/p&gt;

&lt;p&gt;We're also building based on direct user feedback. Our users literally tell our AI "I wish Prodini could..." and we build it within 5 days. That's our promise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Free
&lt;/h2&gt;

&lt;p&gt;We're currently in free beta with 250 credits/month, full access to all features, and all integrations included.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://prodini.ai" rel="noopener noreferrer"&gt;Try Prodini →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'd love to hear from other PMs — what's your biggest pain point with PRD writing? Drop a comment below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>product</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
