<?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: Ernest Essien</title>
    <description>The latest articles on DEV Community by Ernest Essien (@grandkojo).</description>
    <link>https://dev.to/grandkojo</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%2F3532721%2F12b80ca4-203d-4d44-821b-e382106c3527.jpg</url>
      <title>DEV Community: Ernest Essien</title>
      <link>https://dev.to/grandkojo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/grandkojo"/>
    <language>en</language>
    <item>
      <title>How We Built Reef: A Production Incident Agent with Coral, Sentry Webhooks, and Slack</title>
      <dc:creator>Ernest Essien</dc:creator>
      <pubDate>Sun, 31 May 2026 17:14:18 +0000</pubDate>
      <link>https://dev.to/grandkojo/how-we-built-reef-a-production-incident-agent-with-coral-sentry-webhooks-and-slack-ngg</link>
      <guid>https://dev.to/grandkojo/how-we-built-reef-a-production-incident-agent-with-coral-sentry-webhooks-and-slack-ngg</guid>
      <description>&lt;p&gt;When production breaks, the hardest part is not finding data — it is &lt;strong&gt;connecting&lt;/strong&gt; it.&lt;/p&gt;

&lt;p&gt;You open GitHub for recent PRs. Sentry for the error. Slack for what on-call said. Vercel for the deploy that just went out. Then you stare at four tabs and try to line up timestamps in your head.&lt;/p&gt;

&lt;p&gt;We built &lt;strong&gt;Reef&lt;/strong&gt; for the &lt;a href="https://www.wemakedevs.org/hackathons/coral" rel="noopener noreferrer"&gt;Pirates of the Coral-bean&lt;/a&gt; hackathon to automate that workflow: &lt;strong&gt;one investigation, one report&lt;/strong&gt;, with optional remediation based on severity.&lt;/p&gt;

&lt;p&gt;This post is our Captain's Log — how we built it, how we used &lt;a href="https://withcoral.com" rel="noopener noreferrer"&gt;Coral&lt;/a&gt;, and how you can wire the same pattern yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Reef does
&lt;/h2&gt;

&lt;p&gt;Reef is a &lt;strong&gt;production incident intelligence agent&lt;/strong&gt;. You trigger it from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;dashboard&lt;/strong&gt; (natural language or a Vercel deploy link)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Sentry webhook&lt;/strong&gt; (new issue → auto-investigate)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack&lt;/strong&gt; (slash command — coming soon in full production wiring)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reef runs a &lt;strong&gt;stateful investigation loop&lt;/strong&gt;: plan a query → run it through Coral → judge the evidence → repeat until confident → generate a report → post to Slack if triggered by webhook.&lt;/p&gt;

&lt;p&gt;The output includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Root cause hypothesis&lt;/li&gt;
&lt;li&gt;Timeline of iterations&lt;/li&gt;
&lt;li&gt;Suspected PRs&lt;/li&gt;
&lt;li&gt;Citations for every query (&lt;code&gt;coral://query-run/1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Severity score and remediation mode (&lt;code&gt;autonomous_fix&lt;/code&gt; vs &lt;code&gt;human_agent_paired&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why we chose Coral
&lt;/h2&gt;

&lt;p&gt;Before Coral, cross-tool incident triage usually means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Four API clients&lt;/li&gt;
&lt;li&gt;Normalizing different timestamp formats&lt;/li&gt;
&lt;li&gt;Joining in application code&lt;/li&gt;
&lt;li&gt;Stuffing large JSON blobs into an LLM&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Coral flips that model.&lt;/strong&gt; It exposes GitHub, Sentry, Slack, and Vercel as &lt;strong&gt;SQL tables&lt;/strong&gt;. You write queries like:&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="k"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;pr_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;pr_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;error_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;error_level&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pulls&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;issues&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_seen&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;merged_at&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your-org'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your-repo'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fatal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'closed'&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_seen&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;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One query. Two sources. No warehouse. No ETL. Credentials stay on your machine — Coral resolves APIs at query time.&lt;/p&gt;

&lt;p&gt;That temporal join — &lt;strong&gt;errors that appeared after a PR merged&lt;/strong&gt; — is the core insight Reef automates.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture at a glance
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trigger (Dashboard / Sentry webhook / Slack)
        ↓
Investigation Orchestrator (max 5 iterations)
        ↓
Planner (Gemini) ──→ Coral SQL ──→ Judge (Groq)
        ↓                    ↓
   Evidence Store      Query citations
        ↓
Escalation + Severity Gate
        ↓
Report → Slack (for webhooks)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt; Python 3.11+, FastAPI, SQLAlchemy (SQLite dev / Postgres prod)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt; React 19, TypeScript, Vite, Tailwind&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data layer:&lt;/strong&gt; Coral CLI (&lt;code&gt;coral sql&lt;/code&gt;) or mock mode for demos without Coral installed&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI:&lt;/strong&gt; Gemini 2.5 Flash (planner) + Groq Llama 3.3 70B (judge). Falls back to template planner + rules judge if no API keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — Wire Coral sources
&lt;/h2&gt;

&lt;p&gt;Install Coral and register your production tools once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;withcoral/tap/coral

&lt;span class="c"&gt;# From your backend directory&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Fill: GITHUB_TOKEN, GITHUB_OWNER, GITHUB_REPO,&lt;/span&gt;
&lt;span class="c"&gt;#       SENTRY_ORG, SENTRY_TOKEN, SLACK_TOKEN, VERCEL_TOKEN&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .env &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; +a
./scripts/setup_coral_sources.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our setup script adds &lt;strong&gt;github&lt;/strong&gt;, &lt;strong&gt;sentry&lt;/strong&gt;, &lt;strong&gt;slack&lt;/strong&gt;, and &lt;strong&gt;vercel&lt;/strong&gt; (community manifest), then runs smoke queries including the PR↔Sentry join.&lt;/p&gt;

&lt;p&gt;Verify with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;coral sql &lt;span class="s2"&gt;"SELECT schema_name, table_name FROM coral.tables
  WHERE schema_name IN ('github','sentry','slack','vercel') LIMIT 20"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;CORAL_MODE=cli&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt; when you are ready for real data. Use &lt;code&gt;CORAL_MODE=mock&lt;/code&gt; for local demos without Coral installed — Reef returns a coherent checkout-failure story (PR #234 + fatal TypeError + Slack thread).&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — How Reef calls Coral from Python
&lt;/h2&gt;

&lt;p&gt;Reef does not embed Coral as a library. It shells out:&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;# Simplified flow in app/clients/coral_runtime_client.py
&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;coral&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;sql&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;--output&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;json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&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 &lt;strong&gt;query executor&lt;/strong&gt; enforces read-only SQL (&lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;WITH&lt;/code&gt;, &lt;code&gt;EXPLAIN&lt;/code&gt; only), normalizes rows, and stores each run in the database with a citation URI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical investigation sequence:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Iteration&lt;/th&gt;
&lt;th&gt;Coral query purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;coral.tables&lt;/code&gt; — discover connected schemas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;github.pulls&lt;/code&gt; JOIN &lt;code&gt;sentry.issues&lt;/code&gt; — correlate deploys and errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;slack.messages&lt;/code&gt; in &lt;code&gt;#incidents&lt;/code&gt; — on-call context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;vercel.deployments&lt;/code&gt; — deployment timeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;github.teams&lt;/code&gt; or &lt;code&gt;github.collaborators&lt;/code&gt; — ownership for remediation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;planner&lt;/strong&gt; (LLM or template) picks the next query. The &lt;strong&gt;judge&lt;/strong&gt; scores confidence 0.0–1.0. If confidence stays below &lt;code&gt;0.6&lt;/code&gt;, the loop continues.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Sentry webhook → automatic investigations
&lt;/h2&gt;

&lt;p&gt;This was our favorite demo path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sentry fires &lt;code&gt;issue.created&lt;/code&gt; to Reef&lt;/li&gt;
&lt;li&gt;Reef responds &lt;strong&gt;202 Accepted&lt;/strong&gt; immediately (Sentry will not wait for a full investigation)&lt;/li&gt;
&lt;li&gt;Background worker normalizes the payload, resolves the org, runs the orchestrator&lt;/li&gt;
&lt;li&gt;Coral queries run across your stack&lt;/li&gt;
&lt;li&gt;Reef posts a summary to &lt;strong&gt;Slack &lt;code&gt;#incidents&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Configure in Sentry:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Settings → Developer Settings → &lt;strong&gt;New Internal Integration&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Webhook URL: &lt;code&gt;https://your-reef-host/api/v1/webhooks/sentry&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Subscribe to issue events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reef &lt;code&gt;.env&lt;/code&gt;:&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;SLACK_BOT_TOKEN=xoxb-...
SLACK_INCIDENT_CHANNEL=incidents
WEBHOOK_ORGANIZATION_ID=your-reef-org-uuid   # optional but recommended
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test locally:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://127.0.0.1:8000/api/v1/webhooks/sentry &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "action": "created",
    "organization": {"slug": "YOUR_SENTRY_ORG"},
    "data": {
      "issue": {
        "id": "123118378",
        "shortId": "PYTHON-FASTAPI-1",
        "title": "TypeError in checkout payment validation",
        "level": "fatal",
        "project": {"slug": "python-fastapi"}
      }
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;202&lt;/code&gt; with &lt;code&gt;"Investigation queued; report will post to Slack when complete."&lt;/code&gt; — then watch Slack for the finished report.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 — Severity gate and human-in-the-loop
&lt;/h2&gt;

&lt;p&gt;Not every incident should auto-revert a PR.&lt;/p&gt;

&lt;p&gt;Reef scores severity from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Judge confidence&lt;/li&gt;
&lt;li&gt;Blast radius (affected users from Sentry)&lt;/li&gt;
&lt;li&gt;Fatal error penalty&lt;/li&gt;
&lt;li&gt;Missing ownership penalty&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;≤ 0.7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;autonomous_fix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Agent can proceed with remediation workflow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt; 0.7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;human_agent_paired&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Slack approval required before risky actions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;High-severity incidents always keep a human in the loop. Low-risk ones can resolve without paging anyone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Run it yourself
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;backend
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
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; .[dev]
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
uvicorn app.main:app &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;frontend
pnpm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trigger from dashboard:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://127.0.0.1:8000/api/v1/triggers/dashboard &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"query": "Why did checkout fail after the last deploy?"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Simulate all scenarios:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./backend/scripts/simulate_triggers.sh all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;API docs: &lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What we learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stateful loops beat one-shot prompts.&lt;/strong&gt; Investigation is inherently iterative. Persisting every Coral query run with citations made the agent auditable — judges and humans can see &lt;em&gt;why&lt;/em&gt; Reef concluded what it did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coral removed integration busywork.&lt;/strong&gt; We spent time on orchestration, severity gating, and Slack notifications instead of four bespoke API normalizers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQL matches how SREs think.&lt;/strong&gt; Deploys, PRs, errors, threads, and owners are one timeline. Expressing that as JOINs felt natural.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Async webhooks need async workers.&lt;/strong&gt; Returning &lt;code&gt;202&lt;/code&gt; immediately and investigating in the background kept Sentry happy and Slack informed when ready.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Full Slack &lt;code&gt;/reef approve&lt;/code&gt; remediation flow&lt;/li&gt;
&lt;li&gt;Richer LLM planner prompts from prior investigations&lt;/li&gt;
&lt;li&gt;Per-org Coral config isolation at scale&lt;/li&gt;
&lt;li&gt;Observability on Coral query latency and failures&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try Reef
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live URL:&lt;/strong&gt; &lt;a href="https://usereef.grandkojo.my" rel="noopener noreferrer"&gt;https://usereef.grandkojo.my&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Grandkojo/coral_hackers" rel="noopener noreferrer"&gt;https://github.com/Grandkojo/coral_hackers&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Coral docs:&lt;/strong&gt; &lt;a href="https://withcoral.com/docs" rel="noopener noreferrer"&gt;withcoral.com/docs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building an agent that needs data from more than one SaaS tool, start with Coral SQL before you write your fifth API wrapper.&lt;/p&gt;

&lt;p&gt;Questions? Drop them in the comments—I'm happy to share webhook payloads and Coral source setup tips.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built for the WeMakeDevs Coral Hackathon, May 2026. 🏴‍☠️&lt;/em&gt;&lt;/p&gt;

</description>
      <category>coral</category>
      <category>sentry</category>
      <category>slack</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>Hub Map GH: Mapping Ghana's Tech Future with AI &amp; Community</title>
      <dc:creator>Ernest Essien</dc:creator>
      <pubDate>Sun, 01 Mar 2026 23:53:10 +0000</pubDate>
      <link>https://dev.to/grandkojo/hub-map-gh-mapping-ghanas-tech-future-with-ai-community-11fd</link>
      <guid>https://dev.to/grandkojo/hub-map-gh-mapping-ghanas-tech-future-with-ai-community-11fd</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/weekend-2026-02-28"&gt;DEV Weekend Challenge: Community&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Community
&lt;/h2&gt;

&lt;p&gt;Ghana's tech ecosystem is vibrant and growing, but information about its hubs—co-working spaces, incubators, accelerators—is often fragmented across social media and outdated websites. &lt;strong&gt;Hub Map GH&lt;/strong&gt; was built for the builders, entrepreneurs, and innovators in Ghana. It provides a central, real-time source of truth to help anyone find the physical space and community they need to launch their next big idea.&lt;/p&gt;

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

&lt;p&gt;I built a high-performance, real-time directory and map of the Ghanaian tech ecosystem. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🤖 Gemini-Powered AI Matching&lt;/strong&gt;: Don't just browse—ask! Users can type natural language queries like &lt;em&gt;"I'm a hardware founder in Kumasi looking for mentorship"&lt;/em&gt; and get ranked, relevant recommendations via &lt;strong&gt;Gemini Pro&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🤝 Community-Driven Growth&lt;/strong&gt;: Anyone can suggest a new tech hub via a simple, geolocated form. These submissions enter a pending state until verified by an admin, ensuring the directory stays high-quality and collaborative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🗺️ Searchable Ecosystem Map&lt;/strong&gt;: Enhanced filters with real-time search and multi-select tags, making it effortless to navigate through dozens of hubs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔒 Secure Admin Console&lt;/strong&gt;: A protected dashboard for ecosystem leads to verify community submissions, manage cities/tags, and execute batch-updates across the entire directory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚡ Proactive Sync &amp;amp; Caching&lt;/strong&gt;: Multi-layer caching with cross-instance synchronization via Firestore ensures that any administrative change (like deleting or verifying a hub) is instantly reflected site-wide.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📱 Mobile &amp;amp; Native Share&lt;/strong&gt;: Built for a mobile-first nation, including full responsiveness and Native Web Share API integration.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;

&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://hubmapgh.vercel.app" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;hubmapgh.vercel.app&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;




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

&lt;p&gt;You can find the github repository &lt;a href="https://github.com/Grandkojo/hubmapgh" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The project leverages a modern, serverless stack designed for scale and zero cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Framework&lt;/strong&gt;: &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js 14&lt;/a&gt; (App Router) for a lightning-fast frontend.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI&lt;/strong&gt;: &lt;a href="https://aistudio.google.com/" rel="noopener noreferrer"&gt;Google Gemini Pro&lt;/a&gt; provides the natural language reasoning for the recommender, optimized for the ecosystem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Database&lt;/strong&gt;: &lt;a href="https://firebase.google.com/products/firestore" rel="noopener noreferrer"&gt;Firebase Firestore&lt;/a&gt; handles real-time data sync, community submissions, and cross-server cache invalidation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Auth&lt;/strong&gt;: &lt;a href="https://firebase.google.com/products/auth" rel="noopener noreferrer"&gt;Firebase Auth&lt;/a&gt; secures the administrative backbone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Styling&lt;/strong&gt;: &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; with a custom design system inspired by Ghana's national colors (Gold, Red, Green).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Icons/OG&lt;/strong&gt;: Refined, minimalist branding assets generated to provide a premium look and feel.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sustainability &amp;amp; Safety
&lt;/h3&gt;

&lt;p&gt;I implemented IP-based usage tracking in Firestore to enforce a "3 matches per day" limit for AI requests, ensuring the platform remains free to the community while protecting against API abuse.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>showdev</category>
    </item>
    <item>
      <title>New Year, New You: The Void Alchemist Portfolio (Powered by Gemini)</title>
      <dc:creator>Ernest Essien</dc:creator>
      <pubDate>Wed, 28 Jan 2026 08:05:32 +0000</pubDate>
      <link>https://dev.to/grandkojo/new-year-new-you-the-void-alchemist-portfolio-powered-by-gemini-3351</link>
      <guid>https://dev.to/grandkojo/new-year-new-you-the-void-alchemist-portfolio-powered-by-gemini-3351</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/new-year-new-you-google-ai-2025-12-31"&gt;New Year, New You Portfolio Challenge Presented by Google AI&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;Hi, I'm &lt;strong&gt;Ernest Kojo Owusu Essien&lt;/strong&gt;, a software engineer specializing in backend systems and AI. I build systems that think. My goal for 2026 is to push the boundaries of web experiences by merging rigorous backend logic with immersive, cinematic frontend designs. I don't just build websites; I build digital artifacts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portfolio
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ai-portfolio-48210516724.us-central1.run.app" rel="noopener noreferrer"&gt;View Live Portfolio - Cloud Run Link&lt;/a&gt; or &lt;a href="https://portfolio.grandkojo.my" rel="noopener noreferrer"&gt;My Domain&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://ai-portfolio-48210516724.us-central1.run.app"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;




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

&lt;p&gt;I didn't just want a static resume; I wanted a living, breathing digital space that I can manage and evolve without touching code for every content update.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework&lt;/strong&gt;: &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js 14&lt;/a&gt; (App Router) for performance, SEO, and server actions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling&lt;/strong&gt;: &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; for a custom "Void Alchemist" design system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend &amp;amp; Database&lt;/strong&gt;: &lt;strong&gt;Firebase (Firestore &amp;amp; Auth)&lt;/strong&gt;. I moved beyond static data to a real-time database to manage my projects and messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development Process&lt;/strong&gt;: I used &lt;strong&gt;Antigravity by Google&lt;/strong&gt; as my IDE, which was seamless in building the portfolio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI&lt;/strong&gt;: &lt;strong&gt;Gemini 1.5 Flash&lt;/strong&gt; via the Google AI SDK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: &lt;strong&gt;Google Cloud Run&lt;/strong&gt; via Docker containers for serverless scalability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;AI Integration (The Brain)&lt;/strong&gt;: The "Void Alchemist" AI agent is powered by Gemini 1.5 Flash. It uses long-context injection to access documentation and portfolio data, allowing it to answer technical questions accurately, ensuring it never loses context or hallucinates due to missing fragments.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Admin Dashboard (The Control Room)&lt;/strong&gt;: I built a secured Admin Panel (&lt;code&gt;/admin&lt;/code&gt;) using &lt;strong&gt;NextAuth&lt;/strong&gt; and &lt;strong&gt;Firebase&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Message Management&lt;/strong&gt;: Contact form submissions are saved to Firestore and can be read/deleted directly from the dashboard.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dynamic Content&lt;/strong&gt;: My projects and skills are managed via the dashboard, allowing me to update my portfolio instantly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Immersive UI&lt;/strong&gt;: Used Framer Motion for the particle network, floating sigils, and "atmosphere" layer to create a depth-of-field effect that feels premium and cinematic.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dynamic Island Navigation&lt;/strong&gt;: A responsive, app-like floating navigation bar that expands on mobile. It provides seamless access to the new &lt;code&gt;/services&lt;/code&gt; ecosystem while maintaining the immersive aesthetic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What I'm Most Proud Of
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Full-Stack Admin Ecosystem&lt;/strong&gt;: I'm most proud of moving away from hardcoded JSON files. Building a full Admin Dashboard where I can see messages from recruiters and manage my project showcase makes this a true production-grade application, not just a static template.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The AI "Persona"&lt;/strong&gt;: Tuning the Gemini prompt was an art. It switches seamlessly between "Recruiter Mode" (concise, metrics) and "Dev Mode" (technical deep-dives), making it feel like a real extension of myself.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Cloud Run Deployment&lt;/strong&gt;: Getting the Docker container optimized and deployed on Cloud Run was a seamless experience. The app scales down to zero when not in use, which is perfect for a portfolio.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What do you all think about it? Share your thoughts in the comments 😊&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>portfolio</category>
      <category>gemini</category>
    </item>
  </channel>
</rss>
