<?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: Jinish Gupta</title>
    <description>The latest articles on DEV Community by Jinish Gupta (@jinish_gupta_6992e005916c).</description>
    <link>https://dev.to/jinish_gupta_6992e005916c</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%2F3961134%2F288f04a8-4c45-44dc-b984-ade380873e81.png</url>
      <title>DEV Community: Jinish Gupta</title>
      <link>https://dev.to/jinish_gupta_6992e005916c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jinish_gupta_6992e005916c"/>
    <language>en</language>
    <item>
      <title>I Built an AI Deploy Gatekeeper with Coral and Gemini in a Week: Here’s the Route</title>
      <dc:creator>Jinish Gupta</dc:creator>
      <pubDate>Sun, 31 May 2026 12:11:32 +0000</pubDate>
      <link>https://dev.to/jinish_gupta_6992e005916c/i-built-an-ai-deploy-gatekeeper-with-coral-and-gemini-in-a-week-heres-the-route-5chi</link>
      <guid>https://dev.to/jinish_gupta_6992e005916c/i-built-an-ai-deploy-gatekeeper-with-coral-and-gemini-in-a-week-heres-the-route-5chi</guid>
      <description>&lt;p&gt;Every software engineer knows the "pre-deploy anxiety". You've tested your code, your CI is green, and you are ready to click that shiny "Merge" button. But what if there's an ongoing PagerDuty incident on your microservice? What if a third-party API you rely on (like Stripe or AWS) is currently degraded? What if the Sentry error rate spiked 10 minutes ago due to another team's deploy?&lt;/p&gt;

&lt;p&gt;Checking all of this manually takes 10 to 15 minutes, which means most teams simply &lt;em&gt;don't do it&lt;/em&gt;. They merge, pray, and react if things blow up.&lt;/p&gt;

&lt;p&gt;This weekend, I built &lt;strong&gt;Harbourmaster&lt;/strong&gt; — a CLI tool that automatically queries 6 different sources (GitHub, Sentry, Datadog, PagerDuty, StatusGator, and Linear) in seconds, and uses Google’s Gemini 2.5 Flash to give a definitive &lt;strong&gt;[GO]&lt;/strong&gt;, &lt;strong&gt;[CAUTION]&lt;/strong&gt;, or &lt;strong&gt;[HOLD]&lt;/strong&gt; verdict. &lt;/p&gt;

&lt;p&gt;Here is exactly how I built it without writing a mountain of API integration boilerplate, and how you can reproduce it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Secret Sauce: Coral SQL
&lt;/h2&gt;

&lt;p&gt;If I had to write REST API integrations for 6 different platforms, this project would have taken a month. Dealing with pagination, rate limits, OAuth, and different JSON schemas for Sentry, Datadog, and PagerDuty is a nightmare.&lt;/p&gt;

&lt;p&gt;Instead, I used &lt;a href="https://withcoral.com" rel="noopener noreferrer"&gt;Coral&lt;/a&gt; — an incredible open-source SQL engine that lets you query APIs as if they were Postgres databases. &lt;/p&gt;

&lt;p&gt;With Coral installed, I simply authenticated my sources via CLI (&lt;code&gt;coral source add github&lt;/code&gt;, etc.) and then ran standard SQL queries.&lt;/p&gt;

&lt;p&gt;For example, to check for active PagerDuty incidents on my service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urgency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pagerduty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;incidents&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'triggered'&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'acknowledged'&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;created_at&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;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see if all issues tied to my release milestone in Linear were actually closed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state_name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label_names&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;issues&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;label_names&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%v2.4.1%'&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;updated_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bridging Coral to Node.js
&lt;/h3&gt;

&lt;p&gt;I built the CLI using TypeScript and Commander.js. Because Coral handles all the heavy lifting, my data-fetching layer is incredibly thin. Since I’m on Windows, I invoked Coral via WSL using Node's &lt;code&gt;execFile&lt;/code&gt;, ensuring all the SQL queries ran flawlessly.&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;execFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promisify&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:util&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;execFileAsync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;promisify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;execFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;executeCoralQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&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;pathPrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PATH="/home/linuxbrew/.linuxbrew/bin:$PATH"&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;escapedQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/"/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;"&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;stdout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;execFileAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wsl&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pathPrefix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; coral sql "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;escapedQuery&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;stdout&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;By executing 6 of these queries sequentially, Harbourmaster aggregates a snapshot of the entire engineering environment in just a few seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bringing in the AI: Gemini 2.5 Flash
&lt;/h2&gt;

&lt;p&gt;Having a bunch of raw tabular data from Coral is great, but engineers don't want to read tables during a deploy. They want an executive summary.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://aistudio.google.com" rel="noopener noreferrer"&gt;Gemini 2.5 Flash&lt;/a&gt; shines. Flash is ridiculously fast and has excellent reasoning capabilities for structured data.&lt;/p&gt;

&lt;p&gt;I wrote a parser that converts Coral's stdout tables into a clean JSON object representing the "Readiness Snapshot". I then feed this JSON directly to Gemini with a strict system prompt:&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;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
You are the Harbourmaster AI. Your job is to analyze the engineering environment snapshot and determine if it is safe to deploy.
Respond ONLY with a JSON object containing:
- verdict: "GO", "CAUTION", or "HOLD"
- riskScore: Number 0-100 (0=safe, 100=disaster)
- confidence: "low", "medium", or "high"
- reasoning: A 2-4 sentence explanation.
- onCall: Array of on-call engineers.

Here is the snapshot:
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result? The AI notices when the Sentry error rate is 5x the baseline, correlates it with a failed CI run on GitHub, and halts the deploy while alerting the on-call engineer pulled directly from PagerDuty.&lt;/p&gt;




&lt;h2&gt;
  
  
  Formatting for the Terminal
&lt;/h2&gt;

&lt;p&gt;A CLI tool is only as good as its UX. I used &lt;code&gt;chalk&lt;/code&gt; and &lt;code&gt;boxen&lt;/code&gt; to render beautiful, easy-to-read output. Instead of cluttering the terminal with emojis, I used clean ASCII tags like &lt;code&gt;[PASS]&lt;/code&gt;, &lt;code&gt;[WARN]&lt;/code&gt;, and &lt;code&gt;[HOLD]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;╭──────────────────────────────────────────────────╮
│  HARBOURMASTER CHECK                             │
│  service: payments-service   release: v2.4.1     │
╰──────────────────────────────────────────────────╯

  [WARN] Sentry       Error rate 1.4× above 24h baseline. No new fatals.
  [PASS] Datadog      All 5 monitors green.
  [FAIL] PagerDuty    ACTIVE INCIDENT · "Auth service degraded" · P2 · 14m
  ──────────────────────────────────────────────────────────
  Risk Score:  ████████████████████████░░░░░░  78/100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also built a &lt;code&gt;--json&lt;/code&gt; flag so this CLI can run autonomously in a GitHub Action and block PR merges automatically.&lt;/p&gt;




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

&lt;p&gt;By combining Coral's ability to abstract away API complexity and Gemini's ability to reason over structured data, I built a highly complex enterprise workflow agent in a single weekend. &lt;/p&gt;

&lt;p&gt;Deploy readiness doesn't have to be a manual checklist, and building internal tooling doesn't have to require maintaining hundreds of API client libraries.&lt;/p&gt;

&lt;p&gt;You can check out the source code and try it yourself at my &lt;a href="https://github.com/jinishgupta/Harbourmaster" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
