<?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: Geetansh Vikram</title>
    <description>The latest articles on DEV Community by Geetansh Vikram (@geetansh_vikram_836d7f761).</description>
    <link>https://dev.to/geetansh_vikram_836d7f761</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%2F3960431%2F6fd3b3d4-ae10-4d6d-b108-6765a2ff5d5d.png</url>
      <title>DEV Community: Geetansh Vikram</title>
      <link>https://dev.to/geetansh_vikram_836d7f761</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/geetansh_vikram_836d7f761"/>
    <language>en</language>
    <item>
      <title>I Ported a JavaScript Markdown Parser to Rust in 72 Hours — Here's What Actually Broke</title>
      <dc:creator>Geetansh Vikram</dc:creator>
      <pubDate>Mon, 03 Aug 2026 13:26:33 +0000</pubDate>
      <link>https://dev.to/geetansh_vikram_836d7f761/i-ported-a-javascript-markdown-parser-to-rust-in-72-hours-heres-what-actually-broke-1mod</link>
      <guid>https://dev.to/geetansh_vikram_836d7f761/i-ported-a-javascript-markdown-parser-to-rust-in-72-hours-heres-what-actually-broke-1mod</guid>
      <description>&lt;p&gt;&lt;strong&gt;By Geetansh Vikram | Port Mortem 2026 | Track F&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Am I
&lt;/h2&gt;

&lt;p&gt;I'm a third-year Computer Science Engineering student at NIT Silchar. I'm not a professional Rust developer. I'm not a compiler engineer. I haven't shipped a production parser before.&lt;/p&gt;

&lt;p&gt;I'm a student who saw a hackathon about porting code between languages and thought — I know JavaScript, I've been learning Rust, how hard could this be?&lt;/p&gt;

&lt;p&gt;The answer, it turns out, is: significantly harder than I thought. And significantly more rewarding than I expected.&lt;/p&gt;

&lt;p&gt;This is the honest story of how I built marked-rs — a port of the marked.js Markdown parser from JavaScript to Rust — over 72 hours for Port Mortem 2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why marked
&lt;/h2&gt;

&lt;p&gt;The hackathon gives you a pool of eligible repos. When I saw marked.js on the list I immediately knew it was the one.&lt;/p&gt;

&lt;p&gt;Not because it was easy. Because it was &lt;em&gt;interesting&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Markdown parsers look simple from the outside. Headings, bold text, links. How complicated can it be? Then you read the CommonMark specification and discover it has &lt;strong&gt;652 numbered examples&lt;/strong&gt; specifically designed to cover every edge case, every ambiguity, every place where naive implementations break. The emphasis algorithm alone has 131 test cases and a rule — Rule 17 — that exists purely to break regex-based parsers.&lt;/p&gt;

&lt;p&gt;I also knew the context. The Port Mortem hackathon was created because the Bun project merged a 960,000-line Zig-to-Rust rewrite with &lt;strong&gt;13,044 unsafe blocks&lt;/strong&gt; and edited their test suite to make it pass. The judges created this competition to prove AI-assisted porting can be done correctly.&lt;/p&gt;

&lt;p&gt;I wanted to be the proof.&lt;/p&gt;




&lt;h2&gt;
  
  
  Day One — The Confidence Phase
&lt;/h2&gt;

&lt;p&gt;I started with a prompt. A very detailed prompt for Cursor describing the exact architecture, the token types, the pipeline stages, the fuzz harness, everything. I'd done my research. I knew marked.js uses a Lexer → InlineLexer → Renderer pipeline. I knew Rust would let me model this cleanly with enums. I knew I wanted zero unsafe blocks.&lt;/p&gt;

&lt;p&gt;The first build compiled. The first test ran.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;52% CommonMark spec compliance.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I was actually pleased. 52% on day one with a fresh implementation meant the core pipeline was working. Headings passed. Paragraphs passed. Basic emphasis passed.&lt;/p&gt;

&lt;p&gt;I committed that and went to sleep thinking the hard part was behind me.&lt;/p&gt;

&lt;p&gt;It was not behind me.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Emphasis Algorithm — The Six Hours That Ate Me
&lt;/h2&gt;

&lt;p&gt;CommonMark §6.2 describes emphasis parsing with a delimiter stack algorithm. The spec dedicates more text to this than to any other feature. There's a reason for that.&lt;/p&gt;

&lt;p&gt;The naive approach — regex like &lt;code&gt;\*(.+?)\*&lt;/code&gt; — fails immediately on cases like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="ge"&gt;*foo**bar**baz*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What should that produce? The spec says: &lt;code&gt;&amp;lt;em&amp;gt;foo&amp;lt;strong&amp;gt;bar&amp;lt;/strong&amp;gt;baz&amp;lt;/em&amp;gt;&lt;/code&gt;. A regex gives you garbage.&lt;/p&gt;

&lt;p&gt;The correct approach requires a &lt;strong&gt;delimiter stack&lt;/strong&gt; — a data structure that tracks opening and closing delimiter runs, their lengths, and whether they can open or close based on surrounding characters. Then Rule 17: when both the opener and closer have lengths divisible by 3, they only match if their sum is not divisible by 3.&lt;/p&gt;

&lt;p&gt;I implemented this three times.&lt;/p&gt;

&lt;p&gt;The first implementation failed on mid-word underscores — &lt;code&gt;foo_bar_baz&lt;/code&gt; was being emphasized when it shouldn't be. The second implementation fixed that but broke partial consumption — &lt;code&gt;**foo* bar*&lt;/code&gt; was producing wrong nesting. The third implementation finally passed all 131 emphasis examples.&lt;/p&gt;

&lt;p&gt;Six hours. One feature. 131 test cases.&lt;/p&gt;

&lt;p&gt;I went from 64% to 80% compliance in that session.&lt;/p&gt;




&lt;h2&gt;
  
  
  The ReDoS Discovery — The Moment Everything Changed
&lt;/h2&gt;

&lt;p&gt;Around hour 40 I had the differential fuzzer running. It compares our Rust output to Node.js marked output on thousands of random inputs per minute — 130+ runs per second. I let it run overnight.&lt;/p&gt;

&lt;p&gt;When I woke up, the log was clean. Zero divergences.&lt;/p&gt;

&lt;p&gt;But I noticed something in the pathological input corpus I'd built — a specific pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[[[[[[[[[[a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eleven nested opening brackets. I ran it through marked.js:&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;time echo&lt;/span&gt; &lt;span class="s2"&gt;"[[[[[[[[[[[a"&lt;/span&gt; | node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"const m = require('marked'); ..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It hung. I waited. 10 seconds. 20 seconds. 30 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;31 seconds&lt;/strong&gt; for 11 characters.&lt;/p&gt;

&lt;p&gt;That's a ReDoS vulnerability — Regular Expression Denial of Service. The bracket parsing in marked.js has O(2^n) time complexity on crafted inputs. An attacker sending a few hundred nested brackets to any service running marked.js could cause a denial of service.&lt;/p&gt;

&lt;p&gt;I ran the same input through marked-rs:&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;time echo&lt;/span&gt; &lt;span class="s2"&gt;"[[[[[[[[[[[a"&lt;/span&gt; | ./target/release/marked-rs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;1.8 milliseconds.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We had built a &lt;code&gt;MAX_BRACKET_DEPTH&lt;/code&gt; guard early in development specifically because I'd read about this class of vulnerability. It turns out we'd accidentally protected against a real security issue in the original library.&lt;/p&gt;

&lt;p&gt;That moment — comparing 31 seconds to 1.8 milliseconds on the same 11-character input — is the moment I understood why this work matters. It's not academic. It's not performance theater. A parser bug is a security bug.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Numbers That Disappointed Me
&lt;/h2&gt;

&lt;p&gt;I want to be honest about this because the hackathon specifically asks for honest numbers.&lt;/p&gt;

&lt;p&gt;The throughput speedup on large files is &lt;strong&gt;2.4× to 4×&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When I first saw this I was disappointed. Rust is supposed to be fast. Where was my 50× speedup?&lt;/p&gt;

&lt;p&gt;The answer is: marked.js is a mature, heavily optimized JavaScript library. V8 JIT compiles it aggressively. On large file parsing where startup overhead doesn't matter, the speedup is real but modest.&lt;/p&gt;

&lt;p&gt;The startup story is different. &lt;strong&gt;6.6× faster cold start.&lt;/strong&gt; For a CLI tool processing a single README.md file — which is the primary use case — the total wall-clock time difference is 16ms vs 105ms. That's felt.&lt;/p&gt;

&lt;p&gt;And then there's the binary size story: 1.2 MB vs 47 MB of node_modules. That's 39× smaller deployment.&lt;/p&gt;

&lt;p&gt;I rewrote the performance section of my README three times before I found framing that was both honest and compelling. The throughput number alone is underwhelming. The startup number plus the binary size together tell a complete picture.&lt;/p&gt;

&lt;p&gt;Honest numbers over confident claims. That's the standard. I tried to meet it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Decision I'd Take Back
&lt;/h2&gt;

&lt;p&gt;I wrote the renderer using string concatenation first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;render_paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;InlineToken&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;p&amp;gt;{}&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;render_inline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&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;Every block element allocates a new String and concatenates. A 1MB document might trigger thousands of heap allocations during rendering.&lt;/p&gt;

&lt;p&gt;The correct approach — which I switched to midway through — is a single &lt;code&gt;&amp;amp;mut String&lt;/code&gt; buffer passed through the entire render pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;render_paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;InlineToken&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;render_inline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&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;Zero intermediate allocations. One buffer for the entire document.&lt;/p&gt;

&lt;p&gt;I made this switch at hour 55 of 72. It improved throughput by about 15% and dropped RSS noticeably on large documents. If I'd done it from the start — which I should have, it's in the DECISIONS.md entry that tells me to do it from the start — the numbers would have been better from day one.&lt;/p&gt;

&lt;p&gt;Lesson: write the renderer with a buffer from the beginning. The refactor is annoying and the test suite will catch anything you break, but it's better to not need the refactor at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Won the Hackathon (In My Opinion)
&lt;/h2&gt;

&lt;p&gt;Not the code.&lt;/p&gt;

&lt;p&gt;Well, the code matters — 97.5% CommonMark spec compliance and zero unsafe blocks are not trivial achievements. But what I think made the submission genuinely strong was everything around the code:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cryptographic proof.&lt;/strong&gt; The CommonMark spec test file is GPG-signed. Any judge can run &lt;code&gt;gpg --verify tests/spec.json.asc&lt;/code&gt; and mathematically verify we never modified the test files. This is the Bun problem solved at the cryptographic level. Not our word. Math.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honest documentation of failures.&lt;/strong&gt; The 9 failing examples are documented in the README by category: which ones are marked.js intentional divergences, which ones require architectural refactoring we couldn't do in 72 hours, which ones are HTML block edge cases. A submission that claims 100% and can't reproduce it on demand scores below a submission that claims 97.5% and can explain every failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DEFENSE.md.&lt;/strong&gt; A file containing answers to the 5 hardest questions a judge might ask — including a full trace of the emphasis delimiter stack on a specific input. Most teams don't anticipate questions. We wrote the answers before anyone asked them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The FUTURE.md.&lt;/strong&gt; A letter to whoever maintains this project next. What we got right, what we got wrong, what v2.0 should fix first. It sounds small. But judges are engineers. They've all shipped something in 72 hours that they knew had rough edges. A team that documents its own limitations honestly is a team that understood what they built.&lt;/p&gt;




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

&lt;p&gt;Going into this I knew Rust syntax. I could write ownership rules. I understood the borrow checker.&lt;/p&gt;

&lt;p&gt;Coming out of this I understand something different: &lt;strong&gt;what Rust is for&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The zero unsafe constraint wasn't just a hackathon rule. It was a discipline that changed how I wrote code. When you can't use unsafe, you can't paper over your bugs with raw pointer arithmetic. You have to actually understand your data's lifetime. You have to use &lt;code&gt;char_indices()&lt;/code&gt; instead of byte indexing. You have to think about what "the middle of a string" means in Unicode.&lt;/p&gt;

&lt;p&gt;Every time I hit a problem and thought "I could solve this with unsafe" — I found the safe solution. Every single time. And the safe solution was always more readable and caught at least one edge case I hadn't considered.&lt;/p&gt;

&lt;p&gt;The compiler isn't just rejecting your code. It's showing you where your mental model is wrong.&lt;/p&gt;

&lt;p&gt;I also learned what CommonMark actually is. I read more of that specification in 72 hours than most developers read in a career. I know now why emphasis is hard. I know why link reference definitions require two-pass parsing. I know the seven types of HTML blocks and which ones can interrupt a paragraph and which ones can't.&lt;/p&gt;

&lt;p&gt;I understand Markdown now. Not just as a user. As an implementer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hackathon Raptors — What This Community Did For Me
&lt;/h2&gt;

&lt;p&gt;I want to say something genuine here because it's easy to scroll past the thank-you section and I don't want this to be skippable.&lt;/p&gt;

&lt;p&gt;I came into Port Mortem as a student. Not a professional. Not someone with production Rust experience. Not someone who had ever written a parser from scratch.&lt;/p&gt;

&lt;p&gt;The Port Mortem hackathon didn't just give me a problem to solve. It gave me a &lt;strong&gt;reason&lt;/strong&gt; to go deep. The judging criteria — Functionality, Behavioral Equivalence, Code Quality, Innovation — aren't arbitrary categories. They're the same criteria that matter in production systems. Differential fuzzing. Honest benchmarks with p99 and RSS. Decision logs that explain &lt;em&gt;why&lt;/em&gt;, not just &lt;em&gt;what&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I learned more about software engineering in 72 hours than in months of coursework. Not because the coursework is bad — because the hackathon gave me a real target with real stakes and real judges who would actually read my DECISIONS.md.&lt;/p&gt;

&lt;p&gt;The $300 write-up prize being awarded "on insight, not follower count" is the sentence that made me trust this community. A 200-follower account writing something genuinely useful beats a viral thread that says nothing. That's a philosophy I want to carry into everything I build.&lt;/p&gt;

&lt;p&gt;To Hackathon Raptors — thank you for creating a competition where the anti-Bun is the winning move. Where honesty scores higher than confidence. Where documenting your failures is rewarded more than hiding them.&lt;/p&gt;

&lt;p&gt;You gave a third-year student from NIT Silchar a reason to read a 600-page specification at 3am. And genuinely enjoy it.&lt;/p&gt;

&lt;p&gt;That's the gift.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Numbers, One More Time
&lt;/h2&gt;

&lt;p&gt;Because the hackathon asks for honest numbers and this is the write-up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CommonMark spec compliance:  97.5% (636/652)
unsafe blocks:               0 — compiler enforced
Differential fuzz runs:      78,432
Divergences:                 0
Panics:                      0
Startup speedup:             6.6×
Binary size:                 1.2 MB vs 47 MB
Hours of sleep lost:         unknown, not measured
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;marked-rs is live at:&lt;/strong&gt; &lt;code&gt;github.com/geetanshvikram-web/Marked&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live verification portal:&lt;/strong&gt; &lt;code&gt;geetanshvikram-web.github.io/Marked&lt;/code&gt;&lt;br&gt;
*&lt;em&gt;Youtube Video : *&lt;/em&gt; &lt;code&gt;https://www.youtube.com/watch?v=dZaQNHlthrY&lt;/code&gt;&lt;br&gt;
&lt;em&gt;Port Mortem 2026 — Track F — JavaScript → Rust&lt;/em&gt;&lt;/p&gt;

</description>
      <category>portmortem</category>
      <category>hackathonraptors</category>
      <category>marked</category>
    </item>
    <item>
      <title>I Had Never Heard of Cognee. Then I Spent 5 Days Breaking It Wide Open.</title>
      <dc:creator>Geetansh Vikram</dc:creator>
      <pubDate>Sun, 05 Jul 2026 18:00:49 +0000</pubDate>
      <link>https://dev.to/geetansh_vikram_836d7f761/i-had-never-heard-of-cognee-then-i-spent-5-days-breaking-it-wide-open-4j0j</link>
      <guid>https://dev.to/geetansh_vikram_836d7f761/i-had-never-heard-of-cognee-then-i-spent-5-days-breaking-it-wide-open-4j0j</guid>
      <description>&lt;p&gt;&lt;em&gt;By Geetansh Vikram | WeMakeDevs × Cognee Hackathon&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🎥 Watch the full video demo here:&lt;/strong&gt; &lt;a href="https://youtu.be/FW6hriWAz40?si=oAQuVVhegkSAwJ4F" rel="noopener noreferrer"&gt;ContextRot-Bench Demo on YouTube&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I want to be honest with you from the first line: when I saw "WeMakeDevs × Cognee Hackathon," I had to Google what Cognee was.&lt;/p&gt;

&lt;p&gt;I'm a third-year CSE student at NIT Silchar. I spend most of my time grinding competitive programming problems, building Android apps, and occasionally doing something reckless like fine-tuning a 1.5B parameter RL model for a hackathon. I knew what vector stores were. I knew what knowledge graphs were. But "memory layer for AI agents"? I read the landing page three times before it clicked.&lt;/p&gt;

&lt;p&gt;And then it &lt;em&gt;really&lt;/em&gt; clicked. And I couldn't stop.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment I Got Hooked
&lt;/h2&gt;

&lt;p&gt;The hackathon description had this line that stuck with me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Your AI wakes up every morning with no memory of last night."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm a CP guy. I think in test cases. And immediately I started thinking: what's the &lt;em&gt;adversarial&lt;/em&gt; test case for AI memory? Not "can the agent remember a fact" — that's the easy case. The hard case is: &lt;strong&gt;what happens when the agent has been told two different things about the same subject, and one of them is wrong now?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's context rot. And the more I read about it, the more I realized this is not a toy problem. This is the reason support bots quote outdated return policies. This is the reason coding assistants suggest deprecated APIs. This is why "AI memory" demos always show you adding facts but never show you what happens when facts &lt;em&gt;change&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I decided that's what I was going to build. Not another chatbot-with-memory demo. A proof — something with a number attached to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Learning Cognee From Zero
&lt;/h2&gt;

&lt;p&gt;I cloned the repo on day one and started reading.&lt;/p&gt;

&lt;p&gt;Cognee's public API is beautifully simple on the surface:&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;some text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cognify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;           &lt;span class="c1"&gt;# builds the knowledge graph
&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;some question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;improve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;           &lt;span class="c1"&gt;# enriches / resolves contradictions
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four functions. I thought: okay, this is going to be a quick weekend build.&lt;/p&gt;

&lt;p&gt;I was wrong in the best possible way.&lt;/p&gt;

&lt;p&gt;The first thing I learned is that Cognee is not just a vector store with a nice API wrapper. Under the hood it's running a hybrid store — LanceDB for vector embeddings, Kuzu for the graph database — and &lt;code&gt;cognify()&lt;/code&gt; is actually running LLM-based entity and relationship extraction to &lt;em&gt;build a real knowledge graph&lt;/em&gt; from your raw text. That's not a small thing. That means when you add "Alice lives in New York," Cognee doesn't just embed that sentence — it extracts &lt;code&gt;Alice&lt;/code&gt; as an entity, &lt;code&gt;lives in&lt;/code&gt; as a relationship, and &lt;code&gt;New York&lt;/code&gt; as a node, and stores those structural connections in the graph.&lt;/p&gt;

&lt;p&gt;That was the moment I realized this was going to be genuinely interesting to explore.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Assumption That Turned Into a Discovery
&lt;/h2&gt;

&lt;p&gt;My original plan was to build a benchmark: feed two pipelines the same stream of evolving facts (Alice lives in New York → then Chicago → then Seattle), ask them "where does Alice live now," and prove Cognee gives the right answer while a naive vector store hallucinates across all three cities.&lt;/p&gt;

&lt;p&gt;For Cognee's pipeline, I planned to use &lt;code&gt;improve()&lt;/code&gt; — the function the docs describe as "run post-ingestion enrichment, prune stale nodes, and adapt weights based on user feedback." Perfect. That would handle the contradiction resolution.&lt;/p&gt;

&lt;p&gt;So I built a verification script. I ingested a supersession fact. I called &lt;code&gt;improve()&lt;/code&gt;. Then I dumped the raw graph and counted the nodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The stale "New York" node was still there. The count had gone up, not down.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I stared at this for a while. Then I dug into Cognee's source code.&lt;/p&gt;

&lt;p&gt;What &lt;code&gt;improve()&lt;/code&gt; actually does — and this is fascinating once you understand it — is &lt;em&gt;LLM-side reconciliation&lt;/em&gt;. It adds resolution edges and enriches the graph with new context. When you query later, the LLM can &lt;em&gt;reason&lt;/em&gt; its way to the correct answer by reading the contradiction metadata. In a lot of cases, this works fine. The LLM is smart enough to figure out that "Seattle supersedes Chicago supersedes New York."&lt;/p&gt;

&lt;p&gt;But that's not the same as physical deletion. The stale node is still there. If you retrieve a context window with all three locations in it, you're trusting the LLM to resolve the contradiction every single time at query cost. And sometimes it doesn't — I could show that empirically with my naive pipeline, which was doing the same kind of LLM-at-query-time resolution and getting it wrong 75% of the time.&lt;/p&gt;

&lt;p&gt;So I built the missing piece myself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the Deep-Pruning Layer
&lt;/h2&gt;

&lt;p&gt;This is the part I'm most proud of.&lt;/p&gt;

&lt;p&gt;Cognee exposes its underlying graph engine and vector engine as importable clients:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cognee.infrastructure.databases.graph.get_graph_engine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_graph_engine&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cognee.infrastructure.databases.vector.get_vector_engine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_vector_engine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used these to build a custom pruning step that runs after every &lt;code&gt;cognify()&lt;/code&gt; call when a new fact supersedes an old one. The logic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Query the Kuzu graph for any &lt;code&gt;Fact&lt;/code&gt; node where &lt;code&gt;subject&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; match the superseded values&lt;/li&gt;
&lt;li&gt;Delete that node from the graph&lt;/li&gt;
&lt;li&gt;Find all associated vector chunks across every LanceDB table (DocumentChunk, TextSummary, EdgeType) using the node ID&lt;/li&gt;
&lt;li&gt;Delete each chunk individually&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two stores, one atomic operation, zero stale data.&lt;/p&gt;

&lt;p&gt;Along the way I found another silent failure: if you install &lt;code&gt;fastembed&lt;/code&gt; as a standalone package but don't install &lt;code&gt;cognee[fastembed]&lt;/code&gt; (the plugin wrapper), Cognee silently falls back to building the graph without generating any vector embeddings at all. No error, no warning you'd notice, just an empty vector store. That cost me an afternoon and is now documented in the README.&lt;/p&gt;

&lt;p&gt;I also found that Cognee's LLM extraction sometimes rephrases predicates — "lives in" becomes "resides in" or "location" — which would break naive exact-string matching on the predicate field. So instead of matching by predicate, I match by subject + the specific superseded value. That combination is unique enough to identify the stale node reliably, regardless of how the LLM phrased the relationship during extraction.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Benchmark Actually Shows
&lt;/h2&gt;

&lt;p&gt;I built 15 synthetic fact-stream scenarios: job application statuses, user locations, subscription plans, favorite programming languages, flight statuses — domains where facts naturally evolve and contradict each other. Each scenario has a ground truth answer (what's true &lt;em&gt;right now&lt;/em&gt;) and a set of stale values that should not appear in any answer.&lt;/p&gt;

&lt;p&gt;The results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pipeline&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Naive Vector Store&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cognee + Deep-Pruning Layer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;But the number I find more interesting is from the adversarial test. I ran three edge cases specifically designed to stress-test the system:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 1 (Paraphrased query):&lt;/strong&gt; "What city is Alice currently residing in?" instead of "Where does Alice live?"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Naive: &lt;em&gt;"There are multiple cities listed (New York, Seattle, Chicago), but only one can be current. The context does not specify which is current."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Cognee: &lt;em&gt;"Seattle."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Case 2 (Stable fact):&lt;/strong&gt; "What is Alice's favorite color?" — a fact that was never updated&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Naive: "Blue" ✓&lt;/li&gt;
&lt;li&gt;Cognee: "Blue." ✓&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both pipelines get stable facts right. The failure is specifically on contradicted facts. That rules out "Cognee just got lucky" as an explanation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 3 (Recency bias):&lt;/strong&gt; "Where did Alice move to in 2025?" — phrased to give a recency-biased naive pipeline its best chance&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Naive: &lt;em&gt;"There is no information about Alice moving in 2025. The context only provides multiple current locations."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Cognee: &lt;em&gt;"Seattle."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The naive pipeline couldn't resolve this even with a year hint. Because the raw vector chunks don't have meaningful chronological structure — they're just text — there was no signal to prefer Seattle over Chicago over New York. Cognee's graph, with the stale nodes physically absent, had only one answer available.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Open Source Part — Which Honestly Means a Lot to Me
&lt;/h2&gt;

&lt;p&gt;I've been doing competitive programming for two years. I've built Android apps. I've submitted to hackathons. But I had never actually contributed to an open-source project before this hackathon.&lt;/p&gt;

&lt;p&gt;That changed this week.&lt;/p&gt;

&lt;p&gt;While building ContextRot Bench, I spent so much time reading Cognee's migration source code — the &lt;code&gt;GraphitiSource&lt;/code&gt;, &lt;code&gt;Mem0Source&lt;/code&gt;, &lt;code&gt;ZepSource&lt;/code&gt; classes, the COGX memory standard — that I started to actually understand the codebase. Not just the high-level API, but the internals. How &lt;code&gt;import_source.py&lt;/code&gt; orchestrates the migration. How &lt;code&gt;COGXMemory&lt;/code&gt; vs &lt;code&gt;COGXFact&lt;/code&gt; vs &lt;code&gt;COGXEntity&lt;/code&gt; map to different kinds of knowledge.&lt;/p&gt;

&lt;p&gt;When I saw open issues asking for migration tutorials — "Tutorial: Migrate from Graphiti to Cognee," "Tutorial: Migrate from mem0 to Cognee" — I realized I was probably one of the few people outside the core team who had actually read those source files this week.&lt;/p&gt;

&lt;p&gt;So I went and claimed those issues. I have officially &lt;strong&gt;submitted two Pull Requests&lt;/strong&gt; to the main &lt;code&gt;topoteretes/cognee&lt;/code&gt; repository for these migrations, and they are currently under review by the core team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/topoteretes/cognee/pull/3798" rel="noopener noreferrer"&gt;Pull Request: Graphiti Migration Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/topoteretes/cognee/pull/3847" rel="noopener noreferrer"&gt;Pull Request: Mem0 Migration Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing code that will live in a public repository and help other developers — that's different from a hackathon project that only a few judges see. That's something that persists. That compounds. Someone six months from now who's trying to migrate their Graphiti knowledge graph into Cognee might run my tutorial script and it'll just work, and they'll never know a third-year student from NIT Silchar wrote it during a five-day hackathon.&lt;/p&gt;

&lt;p&gt;That thought is genuinely exciting to me in a way that's hard to articulate.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Tell Someone Starting With Cognee
&lt;/h2&gt;

&lt;p&gt;A few things I wish I'd known on day one:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install &lt;code&gt;cognee[fastembed]&lt;/code&gt;, not just &lt;code&gt;fastembed&lt;/code&gt;.&lt;/strong&gt; The plugin wrapper matters and the failure is silent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;improve()&lt;/code&gt; is smarter than deletion, not equivalent to it.&lt;/strong&gt; It adds resolution context; it doesn't remove stale data. Depending on your use case, that might be exactly what you want. For an adversarial benchmark, it wasn't enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The hybrid graph-vector architecture is the real story.&lt;/strong&gt; Most "AI memory" tools are just vector stores with a chat interface. Cognee builds an actual knowledge graph during ingestion — entities, relationships, structural connections — which means your queries can traverse semantic similarity &lt;em&gt;and&lt;/em&gt; graph topology. That's a fundamentally different capability, not a marketing distinction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Go one level below the public API.&lt;/strong&gt; &lt;code&gt;get_graph_engine()&lt;/code&gt; and &lt;code&gt;get_vector_engine()&lt;/code&gt; let you inspect and manipulate the stores directly. That's where the interesting engineering lives.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Five days ago I didn't know what Cognee was.&lt;/p&gt;

&lt;p&gt;Now I've built a benchmark that proves one of its documented failure modes, engineered a custom pruning layer that fixes it, found and documented two bugs in the process, and submitted tutorials back to the open-source repo.&lt;/p&gt;

&lt;p&gt;I don't know if ContextRot Bench will win anything. But I know I understand AI memory systems in a way I didn't before, and I have open-source contributions on my GitHub that I'm genuinely proud of.&lt;/p&gt;

&lt;p&gt;That feels like a good week.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;ContextRot Bench is open source. The benchmark, the deep-pruning layer, and all verification scripts are available on GitHub: *&lt;/em&gt;&lt;a href="https://github.com/Geetansh-12/cognee_hackathon" rel="noopener noreferrer"&gt;Geetansh-12/cognee_hackathon&lt;/a&gt;*&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built for the WeMakeDevs × Cognee Hackathon.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I Built a Relationship Intelligence CRM with Claude Code + Coral — Here's the Route</title>
      <dc:creator>Geetansh Vikram</dc:creator>
      <pubDate>Sat, 30 May 2026 19:55:51 +0000</pubDate>
      <link>https://dev.to/geetansh_vikram_836d7f761/i-built-a-relationship-intelligence-crm-with-claude-code-coral-heres-the-route-2bd4</link>
      <guid>https://dev.to/geetansh_vikram_836d7f761/i-built-a-relationship-intelligence-crm-with-claude-code-coral-heres-the-route-2bd4</guid>
      <description>&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://coral-hackaton.onrender.com" rel="noopener noreferrer"&gt;coral-hackaton.onrender.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/Geetansh-12/coral_hackaton" rel="noopener noreferrer"&gt;github.com/Geetansh-12/coral_hackaton&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most people don't lose touch with contacts because they don't care — they lose touch because relationship data is scattered across Gmail, Slack, LinkedIn, calendar invites, and community servers. I built &lt;strong&gt;Coral CRM&lt;/strong&gt; for the Pirates of the Coral-bean hackathon to show what happens when you stop treating those surfaces as separate apps and start treating them as &lt;strong&gt;one SQL graph&lt;/strong&gt; powered by &lt;a href="https://withcoral.com" rel="noopener noreferrer"&gt;Coral&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post is the reproducible route: problem → architecture → Coral capabilities → live demo → custom Discord source spec.&lt;/p&gt;


&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Relationship intelligence breaks when every channel owns a slice of context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gmail knows what you said&lt;/li&gt;
&lt;li&gt;Calendar knows when you meet&lt;/li&gt;
&lt;li&gt;Slack knows what happened in DMs&lt;/li&gt;
&lt;li&gt;LinkedIn knows job changes&lt;/li&gt;
&lt;li&gt;Discord knows community engagement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An AI agent can't recommend "reach out to Sarah before Thursday's meeting" if it only sees one inbox. You need a &lt;strong&gt;unified graph&lt;/strong&gt; the agent can query safely.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Coral (not just MCP connectors)
&lt;/h2&gt;

&lt;p&gt;MCP connectors are great for tool calls. Coral adds something different: &lt;strong&gt;SQL over APIs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of writing bespoke fetch logic for every source, Coral exposes tables you can &lt;code&gt;JOIN&lt;/code&gt;:&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;health_score&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;public_repos&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;followers&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;contacts&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;JOIN&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;users&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;github_username&lt;/span&gt; &lt;span class="o"&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;username&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;health_score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That query runs in the SQL Explorer of Coral CRM and returns live GitHub profile data joined against local contacts — proof that federated joins aren't vaporware.&lt;/p&gt;

&lt;p&gt;Coral also gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Catalog discovery&lt;/strong&gt; — &lt;code&gt;coral.tables&lt;/code&gt;, &lt;code&gt;coral.columns&lt;/code&gt;, &lt;code&gt;coral.inputs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache/freshness observability&lt;/strong&gt; — &lt;code&gt;coral.query_log&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One auth/retry/pagination layer&lt;/strong&gt; for agent workloads&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architecture in 60 seconds
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Landing / Dashboard / Explorer / AI Chat
              ↓
        Next.js API routes (/api/query, /api/chat, /api/brief)
              ↓
     Demo Mode (mock)  OR  Live Mode (SQLite + coral CLI)
              ↓
   contact_relationship_graph  ← 6 seeded sources + live GitHub/Discord
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Demo Mode&lt;/strong&gt; works with zero API keys — judges can click through immediately.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Live Mode&lt;/strong&gt; seeds SQLite locally and spawns the real &lt;code&gt;coral&lt;/code&gt; binary for federated queries.&lt;/p&gt;

&lt;p&gt;Tech stack: Next.js 14, TypeScript, Tailwind, Gemini (free tier) for AI, better-sqlite3, Coral CLI.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step-by-step: run it yourself
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Geetansh-12/coral_hackaton.git
&lt;span class="nb"&gt;cd &lt;/span&gt;coral_hackaton
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run seed   &lt;span class="c"&gt;# optional — seeds SQLite for live mode&lt;/span&gt;
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Open &lt;strong&gt;&lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/strong&gt;. The app starts in Demo Mode with 34 realistic contacts.&lt;/p&gt;
&lt;h3&gt;
  
  
  Switch to Live Mode
&lt;/h3&gt;

&lt;p&gt;Copy &lt;code&gt;.env.local.example&lt;/code&gt; → &lt;code&gt;.env.local&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;DEMO_MODE=false
GEMINI_API_KEY=your_key
GITHUB_TOKEN=ghp_...
DISCORD_BOT_TOKEN=your_bot_token   # for the custom Discord source
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the Discord source spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;coral &lt;span class="nb"&gt;source &lt;/span&gt;lint ./sources/discord/manifest.yaml
coral &lt;span class="nb"&gt;source &lt;/span&gt;add &lt;span class="nt"&gt;--file&lt;/span&gt; ./sources/discord/manifest.yaml &lt;span class="nt"&gt;--interactive&lt;/span&gt;
coral &lt;span class="nb"&gt;source test &lt;/span&gt;discord
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Coral capabilities we demonstrate (7/7)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. SQL interface over multiple sources
&lt;/h3&gt;

&lt;p&gt;Six seeded tables in &lt;code&gt;sql/schema.sql&lt;/code&gt;: Gmail threads, Calendar events, Slack messages, LinkedIn activity, Twitter activity, Notion contacts.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cross-source JOINs
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;contact_relationship_graph&lt;/code&gt; materialized view LEFT JOINs all six on email and computes a &lt;code&gt;health_score&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Catalog discovery
&lt;/h3&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;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql_reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freshness&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;coral&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Parameter hints
&lt;/h3&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;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;parameter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;coral&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Cache &amp;amp; freshness
&lt;/h3&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;query_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sources_joined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache_hit_rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;avg_ms&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;coral&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Real CLI execution
&lt;/h3&gt;

&lt;p&gt;In Live Mode, &lt;code&gt;/api/query&lt;/code&gt; spawns &lt;code&gt;coral sql --format json&lt;/code&gt; asynchronously — no blocking the Next.js event loop on slow API calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Pluggable source architecture
&lt;/h3&gt;

&lt;p&gt;Settings page shows connector diagnostics per source. We added a &lt;strong&gt;custom Discord source spec&lt;/strong&gt; (see below).&lt;/p&gt;




&lt;h2&gt;
  
  
  The judge demo flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;/dashboard&lt;/code&gt; → click &lt;strong&gt;Judge Demo&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Show &lt;strong&gt;Agent Plan&lt;/strong&gt; and &lt;strong&gt;Coral Capability Cockpit&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;SQL Explorer&lt;/strong&gt; → run the cross-source GitHub JOIN recipe&lt;/li&gt;
&lt;li&gt;Open a contact → generate a &lt;strong&gt;pre-meeting brief&lt;/strong&gt; → export it&lt;/li&gt;
&lt;li&gt;Ask the chat agent: &lt;em&gt;"What Coral capabilities does this demo use?"&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Screenshots are in &lt;code&gt;docs/screenshots/&lt;/code&gt; in the repo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a custom Discord source spec (bounty track)
&lt;/h2&gt;

&lt;p&gt;Coral ships Gmail, GitHub, Slack, etc. — but &lt;strong&gt;Discord wasn't in the catalog&lt;/strong&gt;. For the hackathon "Chart New Waters" track, I wrote a YAML source spec that maps Discord REST API v10 endpoints to SQL tables.&lt;/p&gt;

&lt;p&gt;File: &lt;code&gt;sources/discord/manifest.yaml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tables exposed:&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;SQL table&lt;/th&gt;
&lt;th&gt;Discord API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;discord.current_user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /users/@me&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;discord.guilds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /users/@me/guilds&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;discord.channels&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /guilds/{guild_id}/channels&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;discord.messages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /channels/{channel_id}/messages&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;discord.members&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /guilds/{guild_id}/members&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Auth pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HeaderAuth&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&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;Authorization&lt;/span&gt;
      &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;template&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bot {{input.DISCORD_BOT_TOKEN}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Nested JSON → SQL columns&lt;/strong&gt; using Coral's &lt;code&gt;__&lt;/code&gt; convention:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;author__username&lt;/span&gt;
  &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&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;path&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;author&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Validation workflow:&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;coral &lt;span class="nb"&gt;source &lt;/span&gt;lint ./sources/discord/manifest.yaml
coral &lt;span class="nb"&gt;source &lt;/span&gt;add &lt;span class="nt"&gt;--file&lt;/span&gt; ./sources/discord/manifest.yaml
coral &lt;span class="nb"&gt;source test &lt;/span&gt;discord
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example CRM query once installed:&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;author__username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;discord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;channel_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_CHANNEL_ID'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="nb"&gt;timestamp&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;Full docs: &lt;code&gt;sources/discord/README.md&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Upstream PR target: &lt;code&gt;withcoral/coral&lt;/code&gt; → &lt;code&gt;sources/community/discord/&lt;/code&gt;&lt;/p&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with one table.&lt;/strong&gt; Guilds first, then channels, then messages. Run &lt;code&gt;coral source test&lt;/code&gt; after each addition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nested fields need explicit &lt;code&gt;expr&lt;/code&gt;.&lt;/strong&gt; Don't assume &lt;code&gt;author__username&lt;/code&gt; auto-flattens — declare the path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filters in URL paths&lt;/strong&gt; use &lt;code&gt;{{filter.guild_id}}&lt;/code&gt; — same pattern as other community specs (OSV, dbt Cloud).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo + Live dual mode&lt;/strong&gt; was the right call for hackathon judges — zero friction to explore, real CLI when credentials exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker on Render&lt;/strong&gt; beats serverless for a 150MB Coral binary — one container, authentic live URL.&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ul&gt;
&lt;li&gt;Open upstream PR for the Discord source spec&lt;/li&gt;
&lt;li&gt;Join Discord messages into &lt;code&gt;contact_relationship_graph&lt;/code&gt; on username/nickname&lt;/li&gt;
&lt;li&gt;OAuth device flow for Gmail/Calendar in production&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://coral-hackaton.onrender.com" rel="noopener noreferrer"&gt;coral-hackaton.onrender.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Geetansh-12/coral_hackaton" rel="noopener noreferrer"&gt;Geetansh-12/coral_hackaton&lt;/a&gt;
&lt;/li&gt;
&lt;li&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;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom source guide:&lt;/strong&gt; &lt;a href="https://withcoral.com/docs/guides/write-a-custom-source" rel="noopener noreferrer"&gt;Write a custom source spec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hackathon:&lt;/strong&gt; &lt;a href="https://wemakedevs.org/hackathons/coral" rel="noopener noreferrer"&gt;WeMakeDevs × Coral&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>coral</category>
      <category>ai</category>
      <category>discord</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
