<?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: Swarnnim Agnihotri</title>
    <description>The latest articles on DEV Community by Swarnnim Agnihotri (@swarnnim).</description>
    <link>https://dev.to/swarnnim</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%2F4181016%2Fa22e3dab-4eae-47dc-9529-bbeb7822278f.png</url>
      <title>DEV Community: Swarnnim Agnihotri</title>
      <link>https://dev.to/swarnnim</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/swarnnim"/>
    <language>en</language>
    <item>
      <title>DiffScribe: The Commit Message Writes Itself (Locally)</title>
      <dc:creator>Swarnnim Agnihotri</dc:creator>
      <pubDate>Mon, 26 Oct 2026 20:40:28 +0000</pubDate>
      <link>https://dev.to/swarnnim/diffscribe-the-commit-message-writes-itself-locally-4k8</link>
      <guid>https://dev.to/swarnnim/diffscribe-the-commit-message-writes-itself-locally-4k8</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The most lied-to text box in software
&lt;/h2&gt;

&lt;p&gt;Every developer knows the moment. The work is done, the tests pass, and&lt;br&gt;
then you face the emptiest, most judgmental input field in all of&lt;br&gt;
software: the commit message box. So you type what we all type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fix stuff
updates
final version (2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git keeps perfect records of &lt;em&gt;what&lt;/em&gt; changed. The &lt;em&gt;why&lt;/em&gt; and the &lt;em&gt;what it&lt;br&gt;
means&lt;/em&gt; live in commit messages and changelogs — the two texts written&lt;br&gt;
last, fastest, and worst. &lt;code&gt;git log&lt;/code&gt; archaeology is a running joke&lt;br&gt;
precisely because the record we leave behind is so much worse than the&lt;br&gt;
work it describes.&lt;/p&gt;

&lt;p&gt;The fashionable fix is to paste your diff into a cloud AI and let it&lt;br&gt;
write the message. That works, and it also means shipping your&lt;br&gt;
unreleased code — every secret you forgot to gitignore, every internal&lt;br&gt;
hostname — to someone else's server, per commit, forever.&lt;/p&gt;

&lt;p&gt;I wanted the drafting help without the disclosure. So for this week's&lt;br&gt;
challenge I built &lt;strong&gt;DiffScribe&lt;/strong&gt;: a small command-line tool that reads a&lt;br&gt;
git diff and drafts a conventional-commit message and a changelog entry,&lt;br&gt;
running entirely on my own machine. The AI at its core is an open-weight&lt;br&gt;
model small enough to run on a laptop CPU. The interesting part isn't&lt;br&gt;
that it works — it's &lt;em&gt;how little of the output the model is trusted&lt;br&gt;
with&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What DiffScribe does
&lt;/h2&gt;

&lt;p&gt;Stage some changes, run one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; diffscribe draft
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It parses the diff, builds a compact digest (files, exact +/- counts,&lt;br&gt;
and a few "signal" lines like new function definitions), and asks a&lt;br&gt;
local model — Qwen2.5-0.5B-Instruct (Apache 2.0), served by llama.cpp&lt;br&gt;
(MIT) — for two small wordings: a commit subject, and a handful of&lt;br&gt;
changelog bullets. Then deterministic code takes over again: it&lt;br&gt;
validates both texts, generates the per-file stats body itself, and&lt;br&gt;
assembles the final draft with labels showing exactly which words came&lt;br&gt;
from the model and which came from rules.&lt;/p&gt;

&lt;p&gt;The governing rule of the whole design: &lt;strong&gt;the model words things; code&lt;br&gt;
counts things.&lt;/strong&gt; A language model — especially a half-billion-parameter&lt;br&gt;
one — will happily write "+47 −13" whether or not those numbers are&lt;br&gt;
true. So it is never given the chance. Every number in DiffScribe's&lt;br&gt;
output was counted by parsing the diff.&lt;/p&gt;
&lt;h2&gt;
  
  
  The demo — read it like a skeptic
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;./scripts/demo.sh&lt;/code&gt; drafts for a real staged diff: a README usage&lt;br&gt;
section, a new test file, and a new &lt;code&gt;count_sentences&lt;/code&gt; function added to&lt;br&gt;
a toy text-statistics project. Recorded verbatim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COMMIT MESSAGE:
feat: add docstring for textstat/count.py

Files changed: 3 (+20 -1)
- README.md: +5 -1 (modified)
- tests/test_count.py: +9 -0 (added)
- textstat/count.py: +6 -0 (modified)

CHANGELOG ENTRY (Unreleased):
- Updated README for easy navigation
- Added new installation steps
- Updated tests with new code
- Improved textstat count functionality
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the honest reading, because this output is good &lt;em&gt;and&lt;/em&gt; imperfect in&lt;br&gt;
instructive ways. The subject is well-formed, correctly typed, and&lt;br&gt;
points at the right file — but the diff adds a &lt;em&gt;function&lt;/em&gt;, not a&lt;br&gt;
docstring. The bullets are reasonable paraphrases, except "Added new&lt;br&gt;
installation steps": the README gained a &lt;em&gt;usage&lt;/em&gt; section, not install&lt;br&gt;
steps. Nothing here is malformed, and nothing is quite fabricated —&lt;br&gt;
it's the confident near-miss of a very small model.&lt;/p&gt;

&lt;p&gt;That is exactly the failure profile I designed for. DiffScribe cannot&lt;br&gt;
make a 0.5B model wise. What it can do is bound the blast radius:&lt;br&gt;
validate the &lt;em&gt;shape&lt;/em&gt; (conventional-commit format, length caps, bullet&lt;br&gt;
counts), validate the &lt;em&gt;grounding&lt;/em&gt; (more on that below), generate every&lt;br&gt;
&lt;em&gt;fact-shaped&lt;/em&gt; part itself, and label whatever the model did write so a&lt;br&gt;
human editor — you, before you commit — knows what they're reviewing. A&lt;br&gt;
drafter, supervised. Not an oracle.&lt;/p&gt;
&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Parse.&lt;/strong&gt; The unified diff becomes structured per-file changes:
path, kind (added/modified/deleted/renamed), binary flag, exact
added/deleted counts, and up to three signal lines per file (added
lines that open a definition — &lt;code&gt;def&lt;/code&gt;, &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;export function&lt;/code&gt;…).
One subtlety the tests cover: a content line can &lt;em&gt;look&lt;/em&gt; like a diff
header (&lt;code&gt;---deleted text&lt;/code&gt; inside a removed string), so parsing keys
off the &lt;code&gt;diff --git&lt;/code&gt; file boundaries, not vibes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Digest.&lt;/strong&gt; The parsed change is rendered to a text digest capped at
1,400 characters. Per-file entries truncate before whole files are
dropped, and the exact totals line always survives — the model sees
a faithful summary even for a huge diff, while the stats in the
final output still cover every file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Draft.&lt;/strong&gt; Two separate model calls (subject, then bullets), each
with a one-shot format example, greedy decoding — temperature 0, so
the demo above reproduces byte-for-byte.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate, per text, independently.&lt;/strong&gt; The subject must match the
conventional-commit shape, fit 100 characters, and — the check I'm
proudest of — its scope must be &lt;em&gt;grounded&lt;/em&gt;: if the subject claims
&lt;code&gt;feat(auth):&lt;/code&gt;, the string "auth" must appear in a changed file's
path or signal lines. Bullets must be 1–6 lines, each ≤ 140
characters. Either text can fail on its own and be replaced by a
labeled rule-based draft (type inferred from the change shape, exact
stats included) without discarding the other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assemble.&lt;/strong&gt; Stats body from code, wordings from wherever they
legitimately came from, everything labeled.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is also a &lt;code&gt;--no-llm&lt;/code&gt; mode that skips the model entirely and emits&lt;br&gt;
the pure deterministic draft — vaguer, and never wrong about the facts.&lt;br&gt;
The full recorded transcript of both modes is &lt;code&gt;demo-output.txt&lt;/code&gt; in the&lt;br&gt;
repo.&lt;/p&gt;
&lt;h2&gt;
  
  
  The run where my model lied with a straight face
&lt;/h2&gt;

&lt;p&gt;The grounding check exists because of a specific, embarrassing run&lt;br&gt;
early in development. My first one-shot example — meant only to teach&lt;br&gt;
the output &lt;em&gt;format&lt;/em&gt; — used an auth-flavored sample: a digest about&lt;br&gt;
&lt;code&gt;auth/login.py&lt;/code&gt;, subject &lt;code&gt;fix(auth): reject expired tokens during&lt;br&gt;
login&lt;/code&gt;. I then fed DiffScribe the text-statistics diff, and the model&lt;br&gt;
returned, confidently and in perfect conventional-commit format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat(auth): reject expired tokens during login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no auth code in that diff. There is no login. The model hadn't&lt;br&gt;
read my diff at all; it had copied my example and changed one word.&lt;br&gt;
Every format check passed. This is the tiny-model failure mode that&lt;br&gt;
matters: not gibberish — gibberish is easy to catch — but fluent,&lt;br&gt;
well-formed, entirely wrong.&lt;/p&gt;

&lt;p&gt;Two fixes. The example is now deliberately bland and topically distant&lt;br&gt;
(a README edit), because an example a model might parrot should be one&lt;br&gt;
whose parroting is obvious. And the deterministic grounding check: a&lt;br&gt;
scoped subject whose scope appears nowhere in the diff is rejected and&lt;br&gt;
replaced by the fallback, no matter how pretty it is. There's a&lt;br&gt;
regression test named after the incident&lt;br&gt;
(&lt;code&gt;test_assemble_rejects_leaked_example_subject&lt;/code&gt;). The subject in the&lt;br&gt;
demo — slightly wrong about "docstring", but genuinely about &lt;em&gt;this&lt;/em&gt;&lt;br&gt;
diff — is what the model produces under those constraints.&lt;/p&gt;

&lt;p&gt;A second, quieter reuse bug deserves a sentence: I copied last week's&lt;br&gt;
download script for llama.cpp, and it kept failing with a polite&lt;br&gt;
"Not Found" before a &lt;code&gt;bash -x&lt;/code&gt; trace showed why — the asset name in the&lt;br&gt;
URL had a doubled letter (&lt;code&gt;llama-bb11580…&lt;/code&gt;), a latent typo that had&lt;br&gt;
been sitting in the old script all along. Inherited code inherits bugs.&lt;br&gt;
It also inherits the ability to &lt;em&gt;find&lt;/em&gt; them, because the script was&lt;br&gt;
mine to read. Hold that thought for the next section.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why open innovation matters
&lt;/h2&gt;

&lt;p&gt;This is the third week I've built on the same openly licensed stack —&lt;br&gt;
open weights, an MIT inference engine, a standard-library-only program —&lt;br&gt;
and DiffScribe is the week the compounding became visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reuse is real when the licenses let it be.&lt;/strong&gt; The llama.cpp wrapper,&lt;br&gt;
the greedy-decoding discipline, the labeled-fallback pattern, even the&lt;br&gt;
download scripts came straight from my Week 2 and Week 3 entries.&lt;br&gt;
Nothing had to be re-negotiated, re-purchased, or re-permissioned; the&lt;br&gt;
licenses (Apache 2.0, MIT) were the whole conversation. And when part&lt;br&gt;
of what I reused turned out to be buggy, openness was the debugging&lt;br&gt;
tool: the script was a file on my disk, readable to the last character,&lt;br&gt;
fixable in one line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open weights change who gets to build.&lt;/strong&gt; DiffScribe's model is a&lt;br&gt;
~490 MB file I downloaded once. No account, no API key, no per-commit&lt;br&gt;
bill, no terms-of-service roulette about whether my use case is allowed&lt;br&gt;
this month. A student on a metered connection in Ahmedabad and a staff&lt;br&gt;
engineer at a Fortune 500 company get &lt;em&gt;the same artifact&lt;/em&gt; — and,&lt;br&gt;
because it's just a file, the same ability to inspect what it is and&lt;br&gt;
pin exactly which version produced an output. My demo reproduces&lt;br&gt;
byte-for-byte because the weights, the engine version, and the decoding&lt;br&gt;
settings are all fixed and named in the repo. That reproducibility is&lt;br&gt;
an open-artifact property; a hosted model that silently changes under&lt;br&gt;
you cannot offer it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Openness is also a quality strategy.&lt;/strong&gt; The most important line in&lt;br&gt;
this project is a test that encodes a failure. Small open models fail&lt;br&gt;
in public, cheaply, on your own machine, where you can watch them do it&lt;br&gt;
and engineer around it — validation, grounding, fallbacks. That loop&lt;br&gt;
(watch it fail → bound the failure → write the test) is the actual&lt;br&gt;
craft of building with AI right now, and open tooling makes the loop&lt;br&gt;
tight enough to run in an afternoon.&lt;/p&gt;

&lt;p&gt;On this week's theme — &lt;strong&gt;Ghost in the Machine&lt;/strong&gt; — DiffScribe fits on the honouring-the-past side of the brief, and I want to be plain about the limits of that fit: this is not a ghost-story generator, and it is not an ofrenda builder. What it is, is a keeper of a project's past. Every commit is a trace of work already done, and when those traces are written as "fix stuff", that past is effectively lost — the story of why a change was made fades, just as surely as an unrecorded memory does. DiffScribe tries to keep that history legible, drafting the commit message and changelog entry that let a future reader reconstruct what happened and why, from the diff itself. And there is a literal ghost in this machine, too: a half-billion-parameter open-weight model that lives entirely on my laptop, offline, drafting words in the dark with no server and no internet — a small, local presence that remembers how to phrase the past, and forgets nothing it was never sent.&lt;/p&gt;
&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validation checks shape and grounding, not truth.&lt;/strong&gt; The demo
subject says "docstring" where the diff adds a function; a bullet
says "installation steps" where the README adds usage docs. Plausible
near-misses survive every check I have. Read the draft before you
commit — the tool assumes a human editor, which is why its outputs
are labeled by origin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope grounding is a substring heuristic.&lt;/strong&gt; It catches wholesale
example leakage (its actual job), not subtle irrelevance; a scope of
&lt;code&gt;auth&lt;/code&gt; would also match a path containing &lt;code&gt;author&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Huge diffs are digested, not read.&lt;/strong&gt; Past the digest cap, later
files are summarized by counts alone; the model's wording may skew
toward the early files. The stats body remains exact and complete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback drafting is deliberately vague&lt;/strong&gt; (&lt;code&gt;chore: update code
(3 files, +20 -1)&lt;/code&gt;) — correct about every fact it states, useless as
prose. It is a safety net, not a writer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No partner or hosted technology is used&lt;/strong&gt; — by design, not
omission. The project's entire premise is that this job can be done
locally, offline, with openly licensed parts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;None entered — no partner technology is used, by design (see Honest limitations above). This submission is entered for the overall prize only.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/swarnnim/diffscribe" rel="noopener noreferrer"&gt;https://github.com/swarnnim/diffscribe&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/swarnnim/diffscribe &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;diffscribe
./scripts/download-llamacpp.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./scripts/download-model.sh
./scripts/demo.sh                       &lt;span class="c"&gt;# recorded demo, both modes&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; unittest discover &lt;span class="nt"&gt;-s&lt;/span&gt; tests   &lt;span class="c"&gt;# 50 tests&lt;/span&gt;
git add &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; python3 &lt;span class="nt"&gt;-m&lt;/span&gt; diffscribe draft   &lt;span class="c"&gt;# draft for your own change&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Standard library only; the downloads are the single network touch, and&lt;br&gt;
after that DiffScribe will happily draft commit messages on a plane, in&lt;br&gt;
a tunnel, or wherever your &lt;code&gt;git log&lt;/code&gt; has been embarrassing you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;DiffScribe was built in the open, from open parts, for the most&lt;br&gt;
open-source month of the year. Week 1 taught me to ship small, Week 2&lt;br&gt;
to keep data local, Week 3 to build the guardrails first. Week 4's&lt;br&gt;
lesson: trust the model with the wording, never with the facts — and&lt;br&gt;
write the test for the day it confuses the two.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>SlothPlan: the to-do list that does less</title>
      <dc:creator>Swarnnim Agnihotri</dc:creator>
      <pubDate>Sun, 25 Oct 2026 08:32:23 +0000</pubDate>
      <link>https://dev.to/swarnnim/slothplan-the-to-do-list-that-does-less-18ep</link>
      <guid>https://dev.to/swarnnim/slothplan-the-to-do-list-that-does-less-18ep</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;My to-do list has 47 items. Yours probably does too. And here's the dirty&lt;br&gt;
secret of productivity culture: the list isn't the work. &lt;em&gt;Triaging&lt;/em&gt; the&lt;br&gt;
list is the work — the staring, the re-ordering, the guilt-math of "is&lt;br&gt;
learning Spanish more important than calling the dentist?" I have spent&lt;br&gt;
entire Sunday mornings reorganizing tasks instead of doing them, which is&lt;br&gt;
just procrastination wearing a planner's clothes.&lt;/p&gt;

&lt;p&gt;Every to-do app on the planet answers this with more features: priorities,&lt;br&gt;
tags, Eisenhower matrices, AI scheduling assistants that need your calendar,&lt;br&gt;
your email, your soul, and a $12/month subscription. They make the triage&lt;br&gt;
&lt;em&gt;heavier&lt;/em&gt;. Nobody built the app that does the triage for you and then —&lt;br&gt;
this is the important part — tells you to do less.&lt;/p&gt;

&lt;p&gt;So for "Slow and Steady" I built exactly that: a tool that rewards&lt;br&gt;
laziness by doing the one piece of work you were avoiding (deciding what&lt;br&gt;
matters), capping your day at three things, and granting you written&lt;br&gt;
permission to delete the rest.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SlothPlan&lt;/strong&gt; — a command-line tool where you dump a messy to-do list and&lt;br&gt;
get back three buckets: &lt;strong&gt;DO TODAY&lt;/strong&gt; (max 3, non-negotiable), &lt;strong&gt;DEFER&lt;/strong&gt;&lt;br&gt;
(officially not today's problem), and &lt;strong&gt;DELETE&lt;/strong&gt; (permission granted, let&lt;br&gt;
it go). Each item gets a one-line reason. The judgment comes from a local&lt;br&gt;
open-weight model; the guarantees come from deterministic code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; slothplan triage &lt;span class="nt"&gt;-f&lt;/span&gt; my-messy-todos.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The twist, same as my Week 2 entry: the model, the inference engine, and&lt;br&gt;
all the code run on your own machine. No account. No API key. No&lt;br&gt;
subscription. Your to-do list — which is, let's be honest, a map of your&lt;br&gt;
anxieties — never touches a network.&lt;/p&gt;
&lt;h2&gt;
  
  
  The demo (real transcript, unedited)
&lt;/h2&gt;

&lt;p&gt;I fed it a 12-item Tuesday dump. Here is what the recorded run produced&lt;br&gt;
(full transcript in the repo's &lt;code&gt;demo-output.txt&lt;/code&gt; — nothing cherry-picked):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=== SLOTHPLAN triage ===
method: rule-based fallback (--no-llm was passed; no model was run)

DO TODAY (max 3) — your only decisions today:
  1. Pay electricity bill, due Friday or they cut the power
     -&amp;gt; looks time-sensitive — it names a deadline, payment, or an urgent event
  2. Reply to Priya about the wedding RSVP — deadline is tomorrow
     -&amp;gt; looks time-sensitive — it names a deadline, payment, or an urgent event
  3. Submit the Hacktoberfest Week 3 entry README tonight
     -&amp;gt; looks time-sensitive — it names a deadline, payment, or an urgent event

DEFER — officially not today's problem:
  4. Call dentist to reschedule the appointment
     -&amp;gt; looks time-sensitive — it names a deadline, payment, or an urgent event [moved from today: the 3-item cap keeps your day lazy]
  12. Call mom back — she called twice today
     -&amp;gt; looks time-sensitive — it names a deadline, payment, or an urgent event [moved from today: the 3-item cap keeps your day lazy]

DELETE — permission granted, let it go:
  10. Delete that crypto spam email from "DOUBLE YOUR COINS"
     -&amp;gt; matches a junk pattern — nothing of value is lost
  11. Reorganize my Netflix watchlist
     -&amp;gt; matches a junk pattern — nothing of value is lost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the deterministic mode (&lt;code&gt;--no-llm&lt;/code&gt;), and it's the mode I'd actually&lt;br&gt;
use daily: three real obligations surfaced, the dentist and mom politely&lt;br&gt;
bounced by the cap with an explanation, and the crypto spam and the&lt;br&gt;
Netflix-watchlist reorganization sentenced to death. Twelve decisions I&lt;br&gt;
didn't have to make.&lt;/p&gt;

&lt;p&gt;Now the confession, because the transcript has two runs and I'm not hiding&lt;br&gt;
the first one: when I let the 0.5B model judge all 12 items itself, it&lt;br&gt;
took "rewards laziness" as a personal mission. It &lt;strong&gt;deferred the&lt;br&gt;
electricity bill with the reason "no deadline, start next month"&lt;/strong&gt; — for&lt;br&gt;
a bill due Friday. It called buying milk "spam". It deleted calling my&lt;br&gt;
mother. Every verdict parsed cleanly — 12/12, zero fallbacks — but its&lt;br&gt;
&lt;em&gt;judgment&lt;/em&gt; was a sloth with an agenda. The transcript shows it exactly as&lt;br&gt;
it happened, which is the point of the architecture below: the model's&lt;br&gt;
opinions are suggestions; the deterministic layer holds the guarantees.&lt;/p&gt;
&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Deliberately boring architecture, because boring ships:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parsing&lt;/strong&gt; (stdlib only): every non-empty line is a task; &lt;code&gt;#&lt;/code&gt; comments
and bullet/checkbox markers are stripped. My sample file's header comment
got triaged as a task in the first build — fixed, and there's a test for
it now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judgment&lt;/strong&gt; (&lt;code&gt;slothplan/llm.py&lt;/code&gt;): one verdict per task from
&lt;strong&gt;Qwen2.5-0.5B-Instruct&lt;/strong&gt; (Apache 2.0 open weights, Q4_K_M quantized)
through the &lt;strong&gt;llama.cpp&lt;/strong&gt; binary (MIT). Greedy decoding, ~3 seconds per
item on two CPU cores. The model emits a single bucket word plus a short
reason — a shape small enough for a 0.5B model to hold. This was a
&lt;em&gt;discovery&lt;/em&gt;, not the plan: my first prototype asked for one big JSON
object covering all tasks, and the model hallucinated item numbers 13–20
for a 12-item list and stuffed 12 entries into a 3-item bucket. So the
model judges one task at a time, and structure is enforced by code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforcement&lt;/strong&gt; (&lt;code&gt;slothplan/triage.py&lt;/code&gt;): every verdict is parsed and
validated. Max 3 in "today" — overflow is moved to defer &lt;em&gt;with the
reason printed&lt;/em&gt;, so the cap is visible, not silent. Unparseable verdicts
fall back to keyword rules and are &lt;strong&gt;labeled as fallback in the output&lt;/strong&gt;.
Nothing fails silently, ever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero third-party Python dependencies.&lt;/strong&gt; Python 3.8, two downloaded
files, and it runs. 29 unit tests, all passing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why open innovation matters
&lt;/h2&gt;

&lt;p&gt;This tool &lt;strong&gt;cannot exist&lt;/strong&gt; on a closed API, for two reasons — one&lt;br&gt;
practical, one philosophical.&lt;/p&gt;

&lt;p&gt;Practically: SlothPlan's entire design is a bet that a &lt;em&gt;small&lt;/em&gt; model is&lt;br&gt;
enough. A 0.5B model is honest about what it is — opinionated, sometimes&lt;br&gt;
wrong, occasionally deleting your electricity bill — and the whole&lt;br&gt;
architecture (deterministic guardrails, labeled fallbacks, a &lt;code&gt;--no-llm&lt;/code&gt;&lt;br&gt;
mode) is built around that honesty. You can only build that way if you can&lt;br&gt;
run the model locally, iterate on prompts for free at 3 a.m., and ship&lt;br&gt;
something whose marginal cost per triage is exactly zero. A closed API&lt;br&gt;
charges per token, which means every design decision bends toward fewer,&lt;br&gt;
bigger calls — the opposite of this architecture. And a to-do list is&lt;br&gt;
personal enough that "send it to someone's cloud" was never on the table.&lt;/p&gt;

&lt;p&gt;Philosophically: the open-weights ecosystem is what lets a student with a&lt;br&gt;
$0 budget and a borrowed VM build something in a day that would have been&lt;br&gt;
a research project five years ago. Qwen2.5's weights are public under&lt;br&gt;
Apache 2.0. llama.cpp is MIT. My code is MIT. Anyone can audit every layer,&lt;br&gt;
swap in a bigger GGUF with one flag, or fork the whole thing. That&lt;br&gt;
composability — weights, engine, and app all inspectable and replaceable —&lt;br&gt;
is open innovation working exactly as advertised. The moat isn't the model;&lt;br&gt;
it's the trust that comes from everything being auditable.&lt;/p&gt;
&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;p&gt;I'd rather tell you than have you discover them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The bundled model is &lt;strong&gt;0.5 billion parameters&lt;/strong&gt; — tiny on purpose, so it
runs on any laptop CPU. Its verdicts are &lt;em&gt;opinions&lt;/em&gt;, and sometimes bad
ones (see: the electricity bill incident). Any GGUF drops in via
&lt;code&gt;--model&lt;/code&gt;; a bigger model would judge better.&lt;/li&gt;
&lt;li&gt;Reasons are sometimes missing (&lt;code&gt;(no reason given)&lt;/code&gt;) — the model often
emits just the bucket word despite being shown examples.&lt;/li&gt;
&lt;li&gt;The rule-based fallback is keyword heuristics, not understanding. It
won't catch "renew the thingy before the thing expires."&lt;/li&gt;
&lt;li&gt;Prototype built in a day for a challenge, not a hardened product. 24
items max per run, &lt;code&gt;.txt&lt;/code&gt; input, CLI only.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bonus-points honesty: I have &lt;strong&gt;no first user yet&lt;/strong&gt; — this was built for&lt;br&gt;
the challenge this week, starting Monday morning. The demo above is real&lt;br&gt;
output from the actual tool, and the limitations section is real too.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/swarnnim/slothplan" rel="noopener noreferrer"&gt;https://github.com/swarnnim/slothplan&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./scripts/download-llamacpp.sh
./scripts/download-model.sh
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; slothplan triage &lt;span class="nt"&gt;-f&lt;/span&gt; sample_todos/messy.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you also have a 47-item list you're avoiding: what's the one task on&lt;br&gt;
it you'd secretly love an app to delete for you? That's the roadmap —&lt;br&gt;
a bigger delete bucket.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>SlothPlan: the to-do list that does less</title>
      <dc:creator>Swarnnim Agnihotri</dc:creator>
      <pubDate>Tue, 20 Oct 2026 05:02:46 +0000</pubDate>
      <link>https://dev.to/swarnnim/slothplan-the-to-do-list-that-does-less-20pb</link>
      <guid>https://dev.to/swarnnim/slothplan-the-to-do-list-that-does-less-20pb</guid>
      <description>&lt;h1&gt;
  
  
  SlothPlan: the to-do list that does less
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;My entry for the Hacktoberfest Week 3 "Slow and Steady" challenge — built for International Sloth Day, with open-weight models, zero API costs, and a philosophy I'd actually live by.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;My to-do list has 47 items. Yours probably does too. And here's the dirty secret of productivity culture: the list isn't the work. &lt;em&gt;Triaging&lt;/em&gt; the list is the work — the staring, the re-ordering, the guilt-math of "is learning Spanish more important than calling the dentist?" I have spent entire Sunday mornings reorganizing tasks instead of doing them, which is just procrastination wearing a planner's clothes.&lt;/p&gt;

&lt;p&gt;Every to-do app on the planet answers this with more features: priorities, tags, Eisenhower matrices, AI scheduling assistants that need your calendar, your email, your soul, and a $12/month subscription. They make the triage &lt;em&gt;heavier&lt;/em&gt;. Nobody built the app that does the triage for you and then — this is the important part — tells you to do less.&lt;/p&gt;

&lt;p&gt;So for "Slow and Steady" I built exactly that: a tool that rewards laziness by doing the one piece of work you were avoiding (deciding what matters), capping your day at three things, and granting you written permission to delete the rest.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;SlothPlan&lt;/strong&gt; — a command-line tool where you dump a messy to-do list and get back three buckets: &lt;strong&gt;DO TODAY&lt;/strong&gt; (max 3, non-negotiable), &lt;strong&gt;DEFER&lt;/strong&gt; (officially not today's problem), and &lt;strong&gt;DELETE&lt;/strong&gt; (permission granted, let it go). Each item gets a one-line reason. The judgment comes from a local open-weight model; the guarantees come from deterministic code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; slothplan triage &lt;span class="nt"&gt;-f&lt;/span&gt; my-messy-todos.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The twist, same as my Week 2 entry: the model, the inference engine, and all the code run on your own machine. No account. No API key. No subscription. Your to-do list — which is, let's be honest, a map of your anxieties — never touches a network.&lt;/p&gt;

&lt;h2&gt;
  
  
  The demo (real transcript, unedited)
&lt;/h2&gt;

&lt;p&gt;I fed it a 12-item Tuesday dump. Here is what the recorded run produced (full transcript in the repo's &lt;code&gt;demo-output.txt&lt;/code&gt; — nothing cherry-picked):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=== SLOTHPLAN triage ===
method: rule-based fallback (--no-llm was passed; no model was run)

DO TODAY (max 3) — your only decisions today:
  1. Pay electricity bill, due Friday or they cut the power
     -&amp;gt; looks time-sensitive — it names a deadline, payment, or an urgent event
  2. Reply to Priya about the wedding RSVP — deadline is tomorrow
     -&amp;gt; looks time-sensitive — it names a deadline, payment, or an urgent event
  3. Submit the Hacktoberfest Week 3 entry README tonight
     -&amp;gt; looks time-sensitive — it names a deadline, payment, or an urgent event

DEFER — officially not today's problem:
  4. Call dentist to reschedule the appointment
     -&amp;gt; looks time-sensitive ... [moved from today: the 3-item cap keeps your day lazy]
  12. Call mom back — she called twice today
     -&amp;gt; looks time-sensitive ... [moved from today: the 3-item cap keeps your day lazy]

DELETE — permission granted, let it go:
  10. Delete that crypto spam email from "DOUBLE YOUR COINS"
     -&amp;gt; matches a junk pattern — nothing of value is lost
  11. Reorganize my Netflix watchlist
     -&amp;gt; matches a junk pattern — nothing of value is lost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the deterministic mode (&lt;code&gt;--no-llm&lt;/code&gt;), and it's the mode I'd actually use daily: three real obligations surfaced, the dentist and mom politely bounced by the cap with an explanation, and the crypto spam and the Netflix-watchlist reorganization sentenced to death. Twelve decisions I didn't have to make.&lt;/p&gt;

&lt;p&gt;Now the confession, because the transcript has two runs and I'm not hiding the first one: when I let the 0.5B model judge all 12 items itself, it took "rewards laziness" as a personal mission. It &lt;strong&gt;deleted the electricity bill&lt;/strong&gt;. It called buying milk "spam". It deleted calling my mother. Every verdict parsed cleanly — 12/12, zero fallbacks — but its &lt;em&gt;judgment&lt;/em&gt; was a sloth with an agenda. The transcript shows it exactly as it happened, which is the point of the architecture below: the model's opinions are suggestions; the deterministic layer holds the guarantees.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Deliberately boring architecture, because boring ships:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parsing&lt;/strong&gt; (stdlib only): every non-empty line is a task; &lt;code&gt;#&lt;/code&gt; comments and bullet/checkbox markers are stripped. My sample file's header comment got triaged as a task in the first build — fixed, and there's a test for it now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judgment&lt;/strong&gt; (&lt;code&gt;slothplan/llm.py&lt;/code&gt;): one verdict per task from &lt;strong&gt;Qwen2.5-0.5B-Instruct&lt;/strong&gt; (Apache 2.0 open weights, Q4_K_M quantized) through the &lt;strong&gt;llama.cpp&lt;/strong&gt; binary (MIT). Greedy decoding, ~3 seconds per item on two CPU cores. The model emits a single bucket word plus a short reason — a shape small enough for a 0.5B model to hold. This was a &lt;em&gt;discovery&lt;/em&gt;, not the plan: my first prototype asked for one big JSON object covering all tasks, and the model hallucinated item numbers 13–20 for a 12-item list and stuffed 12 entries into a 3-item bucket. So the model judges one task at a time, and structure is enforced by code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforcement&lt;/strong&gt; (&lt;code&gt;slothplan/triage.py&lt;/code&gt;): every verdict is parsed and validated. Max 3 in "today" — overflow is moved to defer &lt;em&gt;with the reason printed&lt;/em&gt;, so the cap is visible, not silent. Unparseable verdicts fall back to keyword rules and are &lt;strong&gt;labeled as fallback in the output&lt;/strong&gt;. Nothing fails silently, ever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero third-party Python dependencies.&lt;/strong&gt; Python 3.8, two downloaded files, and it runs. 29 unit tests, all passing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why open innovation matters
&lt;/h2&gt;

&lt;p&gt;This tool &lt;strong&gt;cannot exist&lt;/strong&gt; on a closed API, for two reasons — one practical, one philosophical.&lt;/p&gt;

&lt;p&gt;Practically: SlothPlan's entire design is a bet that a &lt;em&gt;small&lt;/em&gt; model is enough. A 0.5B model is honest about what it is — opinionated, sometimes wrong, occasionally deleting your electricity bill — and the whole architecture (deterministic guardrails, labeled fallbacks, a &lt;code&gt;--no-llm&lt;/code&gt; mode) is built around that honesty. You can only build that way if you can run the model locally, iterate on prompts for free at 3 a.m., and ship something whose marginal cost per triage is exactly zero. A closed API charges per token, which means every design decision bends toward fewer, bigger calls — the opposite of this architecture. And a to-do list is personal enough that "send it to someone's cloud" was never on the table.&lt;/p&gt;

&lt;p&gt;Philosophically: the open-weights ecosystem is what lets a student with a $0 budget and a borrowed VM build something in a day that would have been a research project five years ago. Qwen2.5's weights are public under Apache 2.0. llama.cpp is MIT. My code is MIT. Anyone can audit every layer, swap in a bigger GGUF with one flag, or fork the whole thing. That composability — weights, engine, and app all inspectable and replaceable — is open innovation working exactly as advertised. The moat isn't the model; it's the trust that comes from everything being auditable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;p&gt;I'd rather tell you than have you discover them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The bundled model is &lt;strong&gt;0.5 billion parameters&lt;/strong&gt; — tiny on purpose, so it runs on any laptop CPU. Its verdicts are &lt;em&gt;opinions&lt;/em&gt;, and sometimes bad ones (see: the electricity bill incident). Any GGUF drops in via &lt;code&gt;--model&lt;/code&gt;; a bigger model would judge better.&lt;/li&gt;
&lt;li&gt;Reasons are sometimes missing (&lt;code&gt;(no reason given)&lt;/code&gt;) — the model often emits just the bucket word despite being shown examples.&lt;/li&gt;
&lt;li&gt;The rule-based fallback is keyword heuristics, not understanding. It won't catch "renew the thingy before the thing expires."&lt;/li&gt;
&lt;li&gt;Prototype built in a day for a challenge, not a hardened product. 24 items max per run, &lt;code&gt;.txt&lt;/code&gt; input, CLI only.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bonus-points honesty: I have &lt;strong&gt;no first user yet&lt;/strong&gt; — this was built for the challenge this week, starting Monday morning. The demo above is real output from the actual tool, and the limitations section is real too.&lt;/p&gt;

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

&lt;p&gt;Repo: &lt;a href="https://github.com/swarnnim/slothplan" rel="noopener noreferrer"&gt;https://github.com/swarnnim/slothplan&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./scripts/download-llamacpp.sh
./scripts/download-model.sh
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; slothplan triage &lt;span class="nt"&gt;-f&lt;/span&gt; sample_todos/messy.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you also have a 47-item list you're avoiding: what's the one task on it you'd secretly love an app to delete for you? That's the roadmap — a bigger delete bucket.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Why I put my agent's memory in a graph</title>
      <dc:creator>Swarnnim Agnihotri</dc:creator>
      <pubDate>Thu, 15 Oct 2026 16:40:30 +0000</pubDate>
      <link>https://dev.to/swarnnim/why-i-put-my-agents-memory-in-a-graph-1l6b</link>
      <guid>https://dev.to/swarnnim/why-i-put-my-agents-memory-in-a-graph-1l6b</guid>
      <description>&lt;h1&gt;
  
  
  Why I put my agent's memory in a graph
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Built for Graph Hacks (WeMakeDevs × FalkorDB) — the project is &lt;a href="https://github.com/swarnnim/trace" rel="noopener noreferrer"&gt;Trace&lt;/a&gt;, a research agent whose memory is a FalkorDB graph.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every agent I've built has had the same two embarrassing failure modes. First, it forgets. Ask it about the payments outage on Monday, ask again on Thursday, and it stares at you blankly — the conversation is gone. Second, it can't explain itself. When it does answer, you get a confident paragraph with no way to check the work. For a research assistant, those two flaws are fatal.&lt;/p&gt;

&lt;p&gt;The standard fix for forgetting is a vector store: chunk everything, embed it, retrieve by similarity. That solves storage but not structure. "Who decided to pause Project Helios, and why?" is not a similarity question — it's a relationship question. The answer lives in a chain: a &lt;em&gt;person&lt;/em&gt; made a &lt;em&gt;decision&lt;/em&gt;, recorded in a &lt;em&gt;document&lt;/em&gt;, motivated by another &lt;em&gt;document&lt;/em&gt;. Vectors don't do chains. Graphs do.&lt;/p&gt;

&lt;p&gt;So for this hackathon I put the whole memory in a graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  The design
&lt;/h2&gt;

&lt;p&gt;Trace keeps three named graphs on one embedded FalkorDB instance. Named graphs turned out to be the quiet superpower here: &lt;code&gt;trace_mem_sam&lt;/code&gt; is my memory, &lt;code&gt;trace_company&lt;/code&gt; is the shared company brain, &lt;code&gt;trace_blackboard&lt;/code&gt; is where agents coordinate. Tenant isolation with zero extra code — no &lt;code&gt;WHERE tenant_id = ...&lt;/code&gt; sprinkled through every query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Episodic memory&lt;/strong&gt; — what happened — is &lt;code&gt;Episode&lt;/code&gt; nodes linked by &lt;code&gt;FOLLOWS&lt;/code&gt; edges into a temporal chain, with &lt;code&gt;INVOLVES&lt;/code&gt; links to the people and systems mentioned. "What did we discuss about the payments outage?" walks the chain and finds the October 10th episode: root cause was the Redis failover, Dev ran the runbook. With a timestamp. That's the whole trick of cross-session memory — it's just a linked list the agent can traverse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semantic memory&lt;/strong&gt; — what is true — is &lt;code&gt;Belief&lt;/code&gt; nodes with confidence scores and sources. The part I'm proudest of: beliefs are never deleted. When the team rumor "Helios is cancelled" was corrected to "Helios is paused, not cancelled," the new belief got a &lt;code&gt;CONTRADICTS&lt;/code&gt; edge to the old one. The old node stays. Current beliefs are simply the ones with no incoming &lt;code&gt;CONTRADICTS&lt;/code&gt; edge. An agent that can't show you what it used to believe — and why it changed its mind — doesn't really have memory. It has a cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The blackboard&lt;/strong&gt; is how two agents coordinate without messaging each other. A &lt;code&gt;planner&lt;/code&gt; posts a task, a &lt;code&gt;researcher&lt;/code&gt; claims it, findings accumulate as &lt;code&gt;Finding&lt;/code&gt; nodes linked by &lt;code&gt;POSTED&lt;/code&gt; and &lt;code&gt;ABOUT&lt;/code&gt; edges. The handoff chain is itself a traversable path. In the demo, neither the skill graph nor the authorship edge alone could answer "Who knows Redis best?" — PageRank over the skill graph tied across candidates, which the agent says out loud instead of pretending the ranking was decisive. The authorship edge (Ananya wrote the Redis failover runbook) broke the tie. Coordination, not cleverness.&lt;/p&gt;

&lt;h2&gt;
  
  
  One question, worked end to end
&lt;/h2&gt;

&lt;p&gt;"Who decided to pause Helios, and why?"&lt;/p&gt;

&lt;p&gt;The agent finds the decision with a full-text search, then walks the real edges and renders this as the answer's evidence chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="s2"&gt;"Priya Nair"&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MADE&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Decision&lt;/span&gt; &lt;span class="s2"&gt;"Pause Project Helios"&lt;/span&gt;
&lt;span class="n"&gt;Decision&lt;/span&gt; &lt;span class="s2"&gt;"Pause Project Helios"&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DECIDED_IN&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Doc&lt;/span&gt; &lt;span class="s2"&gt;"Helios pause RFC"&lt;/span&gt;
&lt;span class="n"&gt;Decision&lt;/span&gt; &lt;span class="s2"&gt;"Pause Project Helios"&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AFFECTS&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Project&lt;/span&gt; &lt;span class="s2"&gt;"Helios"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the "why" comes from the document the decision points to: burn was unsustainable while the team stabilized the ledger after the September incident — plus a second doc the traversal surfaced, Zoya's launch retro, where onboarding leaked 40% of signups. The answer isn't generated and then justified. The path &lt;em&gt;is&lt;/em&gt; the answer; the text just narrates it.&lt;/p&gt;

&lt;p&gt;My favorite moment in testing was the opposite case: "Is the Postgres upgrade done?" The decision's status is &lt;code&gt;proposed&lt;/code&gt;, not &lt;code&gt;accepted&lt;/code&gt; — and a seeded memory note says "don't state it as done." The agent answers "No" and cites both. An agent that reads its own memory as a guardrail against hallucination is worth more to me than one more point of retrieval recall.&lt;/p&gt;

&lt;h2&gt;
  
  
  What FalkorDB specifically made possible
&lt;/h2&gt;

&lt;p&gt;I could have built this on Postgres with a recursive CTE, and for a demo it would have worked. Three things made the graph database the right call:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Named graphs per user.&lt;/strong&gt; Multigraph isolation meant memory, company knowledge, and agent coordination live side by side with no tenant plumbing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;shortestPath()&lt;/code&gt; as an explainability primitive.&lt;/strong&gt; The evidence chain isn't a feature I bolted on — it's a one-line query. Every answer's "how I got this" is a real path, rendered node by node in the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Algorithms as tools.&lt;/strong&gt; &lt;code&gt;CALL algo.pageRank('Person','KNOWS')&lt;/code&gt; turned "who knows X best" from a vibes question into a computed ranking — with the honest fallback when the math ties.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And pragmatically: FalkorDBLite runs embedded with &lt;code&gt;pip install&lt;/code&gt;, no Docker, no server to babysit. The entire demo — database included — runs on a laptop with zero infrastructure. For a four-day hackathon, that mattered more than any feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest limits
&lt;/h2&gt;

&lt;p&gt;I won't pretend this is a production system. The "LLM" in the demo is a deterministic template composer — it stitches tool outputs into sentences and is physically incapable of inventing facts, which is a feature for a demo and a ceiling for a product. The vector search uses hashed character trigrams, not neural embeddings: it finds text that &lt;em&gt;looks&lt;/em&gt; like your query, not text that &lt;em&gt;means&lt;/em&gt; it. Question routing is keyword rules, not an LLM classifier. The company is fictional. The scale is dozens of nodes, not millions.&lt;/p&gt;

&lt;p&gt;But the core bet survived contact with reality: when memory is a graph, remembering and explaining become the same operation — a traversal. The agent doesn't retrieve context and then reason about it. It reasons &lt;em&gt;by&lt;/em&gt; traversing. That's the idea I'd keep, whatever the scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;em&gt;Trace was built during Graph Hacks (Oct 15–18, 2026). AI coding assistants wrote the implementation under my direction; the architecture, data model, and every design trade-off above are mine. 39/39 tests green, $0 spent.&lt;/em&gt;
&lt;/h2&gt;

</description>
      <category>ai</category>
      <category>graphs</category>
      <category>falkordb</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>LocalLens: private document Q&amp;A that runs 100% on your machine</title>
      <dc:creator>Swarnnim Agnihotri</dc:creator>
      <pubDate>Tue, 13 Oct 2026 05:25:15 +0000</pubDate>
      <link>https://dev.to/swarnnim/locallens-private-document-qa-that-runs-100-on-your-machine-4fb0</link>
      <guid>https://dev.to/swarnnim/locallens-private-document-qa-that-runs-100-on-your-machine-4fb0</guid>
      <description>&lt;h1&gt;
  
  
  LocalLens: private document Q&amp;amp;A that runs 100% on your machine
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;My entry for the Hacktoberfest Week 2 "Side Hustle" challenge — built with open-weight models, zero API costs, and a business model I'd actually try.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Last year a freelancer friend was handed a 34-page client contract and asked to "just flag anything weird" before signing. She couldn't paste it into ChatGPT — the contract literally had a confidentiality clause forbidding exactly that. So she read all 34 pages, twice, on a Sunday.&lt;/p&gt;

&lt;p&gt;That's the gap: millions of people handle documents they &lt;strong&gt;legally or ethically can't upload anywhere&lt;/strong&gt; — client contracts, payroll sheets, patient intake forms, HR files. Their choices are manual reading or breaking confidentiality. There is no third option that costs less than a lawyer.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;LocalLens&lt;/strong&gt; — a command-line tool where you point at a document, ask a question in plain English, and get an answer in seconds. The twist: the AI model, the retrieval, and the inference engine all run on your own machine. No account. No API key. No subscription. Your document never touches a network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; locallens ask &lt;span class="nt"&gt;-f&lt;/span&gt; contract.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"What is the notice period for terminating the agreement?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real output from the included demo (Qwen2.5-0.5B-Instruct, running on a 2-CPU virtual machine — nothing staged):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q: What is the notice period for terminating the agreement?

The notice period for terminating the agreement is thirty (30) days.

(answered from sentence(s) [14, 17] of 32 — run with --show-context to see them)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things I'm proud of here. First, it &lt;strong&gt;cites its sources&lt;/strong&gt; — every answer tells you which sentences it used, and &lt;code&gt;--show-context&lt;/code&gt; prints them, so you can verify before acting on anything. Second, when the answer isn't in the document, it says so instead of inventing one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q: What is the penalty for late payment of invoices?

Not found in the document.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a tool aimed at contracts, that abstention behavior isn't a nice-to-have. It's the whole product. A hallucinating contract assistant is worse than useless — it's a liability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who would pay for this, and why
&lt;/h2&gt;

&lt;p&gt;Three personas, all real:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Freelancers and solo consultants&lt;/strong&gt; reviewing client MSAs and SOWs. They already pay $15–30/month for contract tools that upload their data to someone else's cloud. A one-time $29 purchase that keeps everything local is an easy sell — privacy is the feature, not a checkbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small HR and accounting firms&lt;/strong&gt; answering "what does the policy say about X?" fifty times a week from employee handbooks and tax documents they can't share externally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clinic and legal admin staff&lt;/strong&gt; triaging intake paperwork and engagement letters under confidentiality obligations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The business model I'd actually try: open-source core (this repo, MIT), paid packaged apps — a polished desktop build with PDF/DOCX support and multi-document search at a one-time price. The open core builds trust with exactly the audience that cares about where their data goes; the packaging is what they pay for.&lt;/p&gt;

&lt;p&gt;Bonus-points honesty: I have &lt;strong&gt;no first user yet&lt;/strong&gt; — this was built for the challenge this week. The demo above is real output from the actual tool, and the limitations section below is real too.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Deliberately boring architecture, because boring ships:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval&lt;/strong&gt; (stdlib only): the document is split into sentences and ranked by keyword overlap with the question — stopword-filtered, with naive stemming and a tight-span tie-break so the most &lt;em&gt;focused&lt;/em&gt; sentence wins. No embeddings, no dependencies, runs anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generation&lt;/strong&gt;: the top sentences go into a prompt for &lt;strong&gt;Qwen2.5-0.5B-Instruct&lt;/strong&gt; (Apache 2.0 open weights, Q4_K_M quantized), run through the &lt;strong&gt;llama.cpp&lt;/strong&gt; binary (MIT). Total footprint: ~400 MB, ~20 tokens/sec on two CPU cores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The whole Python project has zero third-party dependencies.&lt;/strong&gt; If you have Python 3.8 and the two downloaded files, it runs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two setup scripts fetch the binary and the model — both free, no accounts. From zero to first answer takes about five minutes, most of it downloading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open weights made this possible
&lt;/h2&gt;

&lt;p&gt;This product &lt;strong&gt;cannot exist&lt;/strong&gt; on a closed API. The entire value proposition — "your document never leaves your machine" — is only credible because every layer is inspectable: the model weights are public (Qwen2.5, Apache 2.0), the inference engine is open source (llama.cpp, MIT), and my own code is MIT. A privacy tool built on a black-box API is a contradiction; built on open weights, it's a verifiable claim. Anyone can audit exactly what runs. That auditability &lt;em&gt;is&lt;/em&gt; the moat — and it's only available because the weights are open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;p&gt;I'd rather tell you than have you discover them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The bundled model is &lt;strong&gt;0.5 billion parameters&lt;/strong&gt; — tiny on purpose, so it runs on any laptop CPU. It's reliable at lookup questions and honest abstention, but convoluted phrasing can stump it, and questions needing synthesis across distant clauses want a bigger model. Any GGUF drops in via &lt;code&gt;--model&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Retrieval is keyword-based, so pure paraphrases can be missed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.txt&lt;/code&gt; input only in v0.1 — PDF/DOCX parsing is roadmap, not reality.&lt;/li&gt;
&lt;li&gt;Prototype, not a hardened product. Verify answers against the cited sentences before acting on them.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Repo: &lt;a href="https://github.com/swarnnim/locallens" rel="noopener noreferrer"&gt;https://github.com/swarnnim/locallens&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./scripts/download-llamacpp.sh
./scripts/download-model.sh
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; locallens ask &lt;span class="nt"&gt;-f&lt;/span&gt; sample_docs/freelance-nda.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"How long do confidentiality obligations survive?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you handle sensitive documents for a living, I'd genuinely like to know: what's the one document you wish you could ask questions about but can't upload anywhere? That's the roadmap.&lt;/p&gt;

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