<?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: Aravindhan V</title>
    <description>The latest articles on DEV Community by Aravindhan V (@aravindhvasudev).</description>
    <link>https://dev.to/aravindhvasudev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F99mvlsfu5tfj9m7ku25d.png</url>
      <title>DEV Community: Aravindhan V</title>
      <link>https://dev.to/aravindhvasudev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aravindhvasudev"/>
    <language>en</language>
    <item>
      <title>We built a second brain that fact-checks itself, running Gemma 4 locally, in one hackathon day Connectore -Hacktoberfest hackathon - Team WInDaChat</title>
      <dc:creator>Aravindhan V</dc:creator>
      <pubDate>Thu, 08 Oct 2026 09:55:31 +0000</pubDate>
      <link>https://dev.to/aravindhvasudev/connectore-hacktoberfest-hackathon-team-windachat-254l</link>
      <guid>https://dev.to/aravindhvasudev/connectore-hacktoberfest-hackathon-team-windachat-254l</guid>
      <description>&lt;p&gt;On Monday you write a note about &lt;strong&gt;negative feedback&lt;/strong&gt; in Control Systems. On Thursday it's &lt;strong&gt;TCP congestion control&lt;/strong&gt; in Computer Networks. On Saturday you save a news snippet about &lt;strong&gt;the RBI raising the repo rate to curb inflation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Three subjects and three notebooks, but it's the same idea every time: &lt;strong&gt;a feedback loop&lt;/strong&gt;. You would probably never notice, because the notes share almost no words.&lt;/p&gt;

&lt;p&gt;That gap is what our team, &lt;strong&gt;WInDaChat&lt;/strong&gt;, built &lt;strong&gt;Connectore&lt;/strong&gt; to close at &lt;strong&gt;Hacktoberfest Hack Day Coimbatore 2026&lt;/strong&gt; (INIT CLUB × iDEA CLUB × MLH).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Connectore&lt;/strong&gt; is a local-first second brain. Gemma 4 links your notes across subjects, tells you the story behind them, and then &lt;strong&gt;checks its own story against your notes for facts it dropped&lt;/strong&gt;. Everything runs on a laptop through Ollama.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🔗 &lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/chuckstone-cpu/hacktoberfest-hack-day-coimbatore-x-init-club-and-idea-club--Team_WInDaChat" rel="noopener noreferrer"&gt;https://github.com/chuckstone-cpu/hacktoberfest-hack-day-coimbatore-x-init-club-and-idea-club--Team_WInDaChat&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The two problems we wanted to solve
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Notes live in silos.&lt;/strong&gt; Tools like Obsidian Smart Connections, Mem and Reflect show "related notes" using embedding similarity. That catches notes that &lt;em&gt;use similar words&lt;/em&gt;. Our three notes above don't, but they describe the &lt;em&gt;same mechanism&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. AI summaries quietly drop things.&lt;/strong&gt; A 2025 study in &lt;em&gt;Royal Society Open Science&lt;/em&gt; found LLM summaries were about 5× more likely than human ones to overgeneralise. An EBU/BBC study found significant issues in 45% of AI-assistant answers about news. A fluent summary isn't necessarily a faithful one. If an app writes you a story from your notes, how do you know it didn't drop the "unless" that mattered?&lt;/p&gt;




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

&lt;h3&gt;
  
  
  1. Connect: links with &lt;em&gt;reasons&lt;/em&gt;, not similarity scores
&lt;/h3&gt;

&lt;p&gt;You write a note in one box. There are no folders and no manual tagging. Behind the scenes, each note costs &lt;strong&gt;two Gemma calls&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tag.&lt;/strong&gt; Gemma 4 returns JSON with a summary, entities (&lt;code&gt;RBI&lt;/code&gt;, &lt;code&gt;TCP&lt;/code&gt;), and &lt;strong&gt;abstract patterns&lt;/strong&gt; like &lt;code&gt;feedback loop&lt;/code&gt;, &lt;code&gt;trade-off&lt;/code&gt; or &lt;code&gt;oscillation&lt;/code&gt;. The patterns are the trick that lets a biology note match a networking note.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shortlist.&lt;/strong&gt; A plain SQL query finds up to 8 older notes sharing an entity or pattern. No AI is involved, so it's fast and free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judge.&lt;/strong&gt; &lt;em&gt;One batched&lt;/em&gt; Gemma call scores every candidate. For each one it returns:

&lt;ul&gt;
&lt;li&gt;a relation (&lt;code&gt;same_idea&lt;/code&gt;, &lt;code&gt;causes&lt;/code&gt;, &lt;code&gt;consequence_of&lt;/code&gt;, &lt;code&gt;continuation&lt;/code&gt;, &lt;code&gt;contradicts&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;a strength from 1 to 5&lt;/li&gt;
&lt;li&gt;a reason that has to cite a fact from &lt;strong&gt;both&lt;/strong&gt; notes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter.&lt;/strong&gt; We keep a link only if it has strength ≥ 4 &lt;em&gt;and&lt;/em&gt; points to a note that was really in the shortlist, so the model can't invent connections.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is an Obsidian-style force graph. Links are coloured by relation type, and hovering one shows Gemma's reason. Click a note and its whole thread lights up. Press &lt;strong&gt;Tell the story&lt;/strong&gt; and Gemma streams a short narrative tying the thread together.&lt;/p&gt;

&lt;p&gt;You can also &lt;strong&gt;drag one note onto another&lt;/strong&gt;. Gemma judges just that pair, then either draws the link or tells you what separates the two notes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Verify: the AI audits its own story
&lt;/h3&gt;

&lt;p&gt;This is the part we're proudest of. Under every story there's a &lt;strong&gt;faithfulness report&lt;/strong&gt; built from two layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A deterministic cue diff (no AI).&lt;/strong&gt; Regexes pull out numbers with units and currency (&lt;code&gt;₹2&lt;/code&gt;, &lt;code&gt;14-day&lt;/code&gt;), dates, negations (&lt;code&gt;not&lt;/code&gt;, &lt;code&gt;never&lt;/code&gt;), conditions (&lt;code&gt;unless&lt;/code&gt;, &lt;code&gt;only if&lt;/code&gt;), bounds (&lt;code&gt;at most&lt;/code&gt;, &lt;code&gt;within&lt;/code&gt;) and obligations (&lt;code&gt;must&lt;/code&gt;, &lt;code&gt;may not&lt;/code&gt;). Anything in the source notes that's missing or changed in the story gets flagged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A fact quiz, in the style of QAGS.&lt;/strong&gt; Gemma pulls up to 8 must-keep facts from the notes, each with an exact quote. Then, in a separate call, it answers a question about each fact &lt;strong&gt;using only the story&lt;/strong&gt;. If the answer is "not stated", the story probably dropped that fact.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each fact gets one of three statuses: &lt;strong&gt;Appears preserved&lt;/strong&gt;, &lt;strong&gt;Needs review&lt;/strong&gt; or &lt;strong&gt;Possible mismatch&lt;/strong&gt;. The source quote is shown right next to the status. We deliberately never show a "verified ✅" badge. The checker can miss things and raise false alarms, and the UI says so.&lt;/p&gt;

&lt;p&gt;We also drop any "fact" whose quote doesn't actually appear in the notes. The verifier is never allowed to cite a source the model made up.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real numbers (measured on our laptop, not estimated)
&lt;/h2&gt;

&lt;p&gt;Setup: Windows 11, Node 24, Ollama 0.40.1, &lt;code&gt;gemma4:e4b&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;About 48–50 tokens/s&lt;/strong&gt; once the model is warm.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tagging takes 1.2–1.7 s per note. A full add (tag + link) takes 3–6 s.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Seeding 10 demo notes produced a clean graph:

&lt;ul&gt;
&lt;li&gt;control systems, blood sugar regulation, TCP, the RBI repo rate and supply/demand all linked as &lt;code&gt;same_idea&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;deadlock linked to two-phase locking&lt;/li&gt;
&lt;li&gt;gradient descent linked to control systems (oscillation!)&lt;/li&gt;
&lt;li&gt;our &lt;strong&gt;photosynthesis&lt;/strong&gt; control note correctly got &lt;strong&gt;zero links&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The story streams&lt;/strong&gt; its first words in about 0.5 s. A 6-note story finishes in under 10 s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The verifier on a deliberately sabotaged story&lt;/strong&gt; (a library rule with "at most twice", "unless someone else has reserved the book" and "₹2 per day after the 14-day loan period", rewritten to drop them):

&lt;ul&gt;
&lt;li&gt;It flagged all four cues.&lt;/li&gt;
&lt;li&gt;It marked 3 facts as possible mismatches and 1 as needs review, in about 7.7 s.&lt;/li&gt;
&lt;li&gt;The honest 6-note story scored all 7 facts as preserved, with 0 cue issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Gemma 4&lt;/strong&gt; (&lt;code&gt;gemma4:e4b&lt;/code&gt;) via &lt;strong&gt;Ollama&lt;/strong&gt;, fully local&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Node.js + Express, the official &lt;code&gt;ollama&lt;/code&gt; JS client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Zod&lt;/strong&gt;: the schema goes to Ollama as &lt;code&gt;format&lt;/code&gt;, and the reply is validated against it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB&lt;/td&gt;
&lt;td&gt;SQLite via Node's built-in &lt;code&gt;node:sqlite&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;React + Vite + Tailwind, &lt;code&gt;react-force-graph-2d&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Things that bit us (and what we learned)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Pass &lt;code&gt;num_ctx&lt;/code&gt; on every single call.&lt;/strong&gt; Ollama's default context window depends on your VRAM and can be as small as 4K on a laptop. Long prompts get &lt;strong&gt;silently truncated&lt;/strong&gt;, and you get no error, just worse answers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Structured output needs a seatbelt.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We send a JSON schema via &lt;code&gt;format&lt;/code&gt;, use &lt;code&gt;think: false&lt;/code&gt; and temperature 0, validate every reply with Zod, and &lt;strong&gt;retry once&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The very first call while the model was loading came back empty. That's why the retry exists, and why the server now warms the model up on startup (the first call took about 32 s before that).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&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;callJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&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;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toJSONSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;CALL_DEFAULTS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* log and retry once */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LlmError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Gemma call failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Models love vague answers.&lt;/strong&gt; Gemma first tagged unrelated notes with the pattern &lt;code&gt;"cause and effect"&lt;/code&gt;, which linked everything to everything. Two changes fixed it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a short list of preferred patterns&lt;/li&gt;
&lt;li&gt;an explicit "never use vague patterns like &lt;em&gt;cause and effect&lt;/em&gt;" rule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Ask for the difference, not just the reason.&lt;/strong&gt; When drag-to-connect &lt;em&gt;rejected&lt;/em&gt; a pair, Gemma's &lt;code&gt;reason&lt;/code&gt; field often argued &lt;em&gt;for&lt;/em&gt; the link ("both describe feedback…"), which made the rejection message confusing. We added a separate &lt;code&gt;difference&lt;/code&gt; field and show that instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Batch your LLM calls.&lt;/strong&gt; Judging 8 candidates one at a time would mean 9+ calls per note, which is a minute or more on a laptop. One batched call keeps adding a note at a few seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Test a fresh clone before the deadline.&lt;/strong&gt; &lt;code&gt;better-sqlite3&lt;/code&gt; tried to compile from source on a clean Windows install and failed without the C++ build tools. Switching to Node's built-in &lt;code&gt;node:sqlite&lt;/code&gt; removed the native dependency entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Cut scope honestly.&lt;/strong&gt; We originally planned a "KV Cache Lab" to measure how Gemma's facts survive when its KV cache is quantized to q8/q4. Research showed Gemma 4 is unusually sensitive to that, which makes it a fascinating question. But we couldn't run it properly in the time, so we cut it rather than ship made-up numbers. It's first on our "what's next" list.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The KV Cache Lab:&lt;/strong&gt; run the same story + fact quiz with Gemma's KV cache at f16 / q8_0 / q4_0, and measure memory against how many facts survive&lt;/li&gt;
&lt;li&gt;Import notes from a URL or PDF&lt;/li&gt;
&lt;li&gt;Weekly digests of new threads&lt;/li&gt;
&lt;li&gt;Quizzes generated from your own threads&lt;/li&gt;
&lt;li&gt;A small Gemma 4 running offline on Android&lt;/li&gt;
&lt;/ul&gt;




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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma4:e4b
git clone https://github.com/chuckstone-cpu/hacktoberfest-hack-day-coimbatore-x-init-club-and-idea-club--Team_WInDaChat.git
&lt;span class="nb"&gt;cd &lt;/span&gt;hacktoberfest-hack-day-coimbatore-x-init-club-and-idea-club--Team_WInDaChat
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
npm run seed   &lt;span class="c"&gt;# loads 10 demo notes through the real pipeline&lt;/span&gt;
npm run dev    &lt;span class="c"&gt;# UI at http://localhost:5173&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's MIT licensed, and PRs are welcome. It &lt;em&gt;is&lt;/em&gt; Hacktoberfest, after all 🎃&lt;/p&gt;

&lt;p&gt;Built by &lt;strong&gt;Team WInDaChat&lt;/strong&gt;: V Aravindhan, Siva Krithick, Sahesh Karthikeyan and Sathyanarayanan.&lt;/p&gt;

&lt;p&gt;If you've ever had two notes from completely different classes turn out to be the same idea, tell us about it in the comments 👇&lt;/p&gt;

</description>
      <category>ai</category>
      <category>hacktoberfest</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
