<?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: SHWETANK-SAURABH</title>
    <description>The latest articles on DEV Community by SHWETANK-SAURABH (@shwetanksaurabh).</description>
    <link>https://dev.to/shwetanksaurabh</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%2F4155873%2F6398045e-b3b7-421e-9455-6ba6df8903f2.jpg</url>
      <title>DEV Community: SHWETANK-SAURABH</title>
      <link>https://dev.to/shwetanksaurabh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shwetanksaurabh"/>
    <language>en</language>
    <item>
      <title>GhostRepo: A Local Autopsy for Abandoned Repositories, with Gemma Doing the Talking</title>
      <dc:creator>SHWETANK-SAURABH</dc:creator>
      <pubDate>Sat, 31 Oct 2026 22:03:22 +0000</pubDate>
      <link>https://dev.to/shwetanksaurabh/ghostrepo-a-local-autopsy-for-abandoned-repositories-with-gemma-doing-the-talking-j0e</link>
      <guid>https://dev.to/shwetanksaurabh/ghostrepo-a-local-autopsy-for-abandoned-repositories-with-gemma-doing-the-talking-j0e</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;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Every abandoned repository has a story, but the people who could tell it have usually moved on. What's left is a stale README, a few hundred commits, and a dependency file nobody has touched in years.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GhostRepo&lt;/em&gt; investigates repositories like these on your own machine and helps you plan their resurrection.&lt;/p&gt;

&lt;p&gt;Point it at a local repository, choose &lt;strong&gt;Summon Ghost&lt;/strong&gt;, and let it perform an autopsy.&lt;/p&gt;

&lt;p&gt;Here's what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Examines programming languages, frameworks, Git history, dependencies, configuration, documentation, and tests. It also runs Ruff on Python code.&lt;/li&gt;
&lt;li&gt;Reports verified findings, each backed by evidence, a source file, and its detection method.&lt;/li&gt;
&lt;li&gt;Uses a locally running Gemma model to interpret those facts, keeping AI-generated findings separate and clearly labeled.&lt;/li&gt;
&lt;li&gt;Builds a prioritized resurrection plan and drafts a new README.&lt;/li&gt;
&lt;li&gt;Proposes code changes as downloadable diffs for you to review. GhostRepo never applies patches automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My interpretation of the theme is simple: a quiet repository is a ghost, not necessarily a corpse.&lt;/p&gt;

&lt;p&gt;A project with no recent commits might be finished, stable, or simply waiting for someone to discover it. GhostRepo deliberately avoids declaring a project abandoned based on commit age alone.&lt;/p&gt;

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

&lt;p&gt;GhostRepo is designed as a local-first tool. It doesn't require accounts or a hosted AI service, and its API is intended to remain accessible only locally.&lt;/p&gt;

&lt;p&gt;To try it yourself:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Pull the model:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma3:4b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Clone the repository:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/SHWETANK-SAURABH/GhostRepo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;GhostRepo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Start the application:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Clone a project you want to investigate into &lt;code&gt;workspace/&lt;/code&gt; and open:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See the repository README for complete setup instructions and configuration details.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The complete source code is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/SHWETANK-SAURABH/GhostRepo" rel="noopener noreferrer"&gt;GhostRepo — View Source Code&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/SHWETANK-SAURABH" rel="noopener noreferrer"&gt;
        SHWETANK-SAURABH
      &lt;/a&gt; / &lt;a href="https://github.com/SHWETANK-SAURABH/GhostRepo" rel="noopener noreferrer"&gt;
        GhostRepo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;GhostRepo&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Every abandoned repository has a story. Let's bring it back to life.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;GhostRepo investigates a local repository and helps you prepare a safe recovery plan. Deterministic analyzers collect the facts. A model running on your own machine can then interpret them. Nothing is uploaded, and nothing in the scanned repository is executed or changed.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Investigates&lt;/strong&gt; languages, frameworks, Git history, dependencies, configuration, documentation and tests, and runs Ruff on Python code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reports verified findings&lt;/strong&gt; with severity, evidence, source file, confidence and detection method.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adds AI interpretation&lt;/strong&gt; from a local Ollama model, kept separate from verified findings and labeled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Builds a resurrection plan&lt;/strong&gt; in priority order, with verification steps and rough effort estimates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drafts a README&lt;/strong&gt; from verified facts and marks anything it could not confirm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proposes patches&lt;/strong&gt; as diffs you review and approve before download. GhostRepo never applies them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exports reports&lt;/strong&gt; as Markdown or JSON, with secrets…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/SHWETANK-SAURABH/GhostRepo" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

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

&lt;p&gt;The stack is &lt;em&gt;FastAPI, SQLite, Next.js, and Gemma 3 (&lt;code&gt;gemma3:4b&lt;/code&gt;)&lt;/em&gt;, running locally through Ollama.&lt;/p&gt;

&lt;p&gt;One of the most important architectural decisions was this:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The language model never reads the repository directly.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Instead, GhostRepo follows a controlled evidence-based pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;Collect facts:&lt;/em&gt; Plain Python analyzers inspect repository metadata, Git history, dependency manifests, configuration, documentation, and tests. They don't execute the repository's code.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Build evidence:&lt;/em&gt; The collected facts become structured evidence records with unique IDs. Sensitive values are redacted.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Ask Gemma:&lt;/em&gt; The local model receives the evidence as untrusted data and is instructed to cite evidence IDs when making claims.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Validate everything:&lt;/em&gt; The application validates the response schema, redacts the output again, checks every evidence reference, and applies additional claim-validation rules.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Present results:&lt;/em&gt; Verified findings and AI-generated interpretations appear separately. The model cannot turn an unsupported guess into a verified finding.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If Ollama isn't running, deterministic findings, recovery plans, README drafts, patches, and exports still work.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Testing the Real Model Taught Me
&lt;/h2&gt;

&lt;p&gt;Testing with mocks was useful, but testing against the actual model exposed problems I hadn't anticipated.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;1. Structured output wasn't actually working.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ollama rejected my original JSON schema with &lt;code&gt;failed to parse grammar&lt;/code&gt;. A JSON-mode fallback hid the error and sometimes returned &lt;code&gt;{}&lt;/code&gt;, which only became obvious when analyzing larger repositories.&lt;/p&gt;

&lt;p&gt;I simplified the schema and added a live integration test that calls the real model without relying on that fallback.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;2. The model ignored some of my instructions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I explicitly told Gemma not to treat commit age as evidence of abandonment. It still rated inactivity as critical and occasionally claimed vulnerabilities or abandonment without supporting evidence.&lt;/p&gt;

&lt;p&gt;Repeating the prompt more firmly wasn't a solution.&lt;/p&gt;

&lt;p&gt;Instead, I added code-level claim policies that reject unsupported conclusions. Live runs subsequently discarded findings such as "Long Inactivity" and "Outdated Dependencies" when the available evidence didn't justify them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;3. Test fixtures looked like leaked credentials.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Some example credentials in test folders were being classified as critical secrets.&lt;/p&gt;

&lt;p&gt;I adjusted the detection policy so matches in test and example paths receive lower severity rather than being treated like confirmed production credential leaks.&lt;/p&gt;
&lt;h2&gt;
  
  
  Security: Treat Every Repository as Untrusted
&lt;/h2&gt;

&lt;p&gt;A repository can contain more than just source code.&lt;/p&gt;

&lt;p&gt;For example, its &lt;code&gt;.git/config&lt;/code&gt; can specify programs for Git to execute through certain configuration settings.&lt;/p&gt;

&lt;p&gt;To address this, GhostRepo overrides Git settings such as &lt;code&gt;core.fsmonitor&lt;/code&gt; during Git operations. I also added a hostile Git configuration test to verify that those programs aren't executed.&lt;/p&gt;

&lt;p&gt;Ruff runs with &lt;code&gt;--isolated&lt;/code&gt;, preventing repository-specific configuration from silently changing the checks.&lt;/p&gt;

&lt;p&gt;The broader principle is that repository contents are data to inspect, not instructions to follow.&lt;/p&gt;
&lt;h2&gt;
  
  
  Current Limitations
&lt;/h2&gt;

&lt;p&gt;GhostRepo is an MVP, and there are some deliberate limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Patches currently add missing project files and &lt;code&gt;.gitignore&lt;/code&gt; entries. It doesn't yet propose arbitrary source-code fixes.&lt;/li&gt;
&lt;li&gt;Static analysis currently covers Python through Ruff.&lt;/li&gt;
&lt;li&gt;Dependency manifests are inspected offline, so GhostRepo cannot confirm whether a package is currently outdated or vulnerable.&lt;/li&gt;
&lt;li&gt;Tests are discovered but not automatically executed.&lt;/li&gt;
&lt;li&gt;Gemma can still produce incorrect interpretations. A valid evidence citation proves that the evidence exists, not that the model interpreted it correctly.&lt;/li&gt;
&lt;li&gt;I've tested &lt;code&gt;gemma3:4b&lt;/code&gt;; compatibility with other Ollama models still needs further testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building With an AI Coding Agent
&lt;/h2&gt;

&lt;p&gt;I built GhostRepo with the assistance of a hosted AI coding agent, working from my own project brief and architecture decisions.&lt;/p&gt;

&lt;p&gt;That distinction matters: the coding assistant helped build the application, but the open-weight model running inside GhostRepo performs the actual repository analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;People revive old code for many reasons, and some of that code is private: a former employer's internal tool, a client project, or a personal side project with credentials still buried somewhere inside it.&lt;/p&gt;

&lt;p&gt;Sending an entire repository to a cloud AI API just to understand its current condition isn't always a good trade.&lt;/p&gt;

&lt;p&gt;Running Gemma locally changes that.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repository evidence stays on the developer's machine.&lt;/li&gt;
&lt;li&gt;Inference doesn't require an external AI account.&lt;/li&gt;
&lt;li&gt;There are no per-token API charges.&lt;/li&gt;
&lt;li&gt;Developers can inspect and modify the analysis pipeline.&lt;/li&gt;
&lt;li&gt;The model can be replaced with another compatible Ollama model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also restricted the configured Ollama endpoint to a local allowlist, so changing a setting can't silently redirect repository evidence to an arbitrary external service.&lt;/p&gt;

&lt;p&gt;During a full walkthrough with the real model, I sampled the application's processes 240 times and observed no connections to non-loopback addresses.&lt;/p&gt;

&lt;p&gt;That's useful evidence, but not proof of complete network isolation. I haven't yet tested the entire application with the network physically disconnected.&lt;/p&gt;

&lt;p&gt;There's another reason open innovation matters here.&lt;/p&gt;

&lt;p&gt;A tool designed to resurrect abandoned software shouldn't depend on a proprietary service that might itself disappear someday.&lt;/p&gt;

&lt;p&gt;With the model downloaded locally, GhostRepo can continue working without an account, an API subscription, or an external inference service.&lt;/p&gt;

&lt;p&gt;One licensing distinction is worth mentioning: Gemma's weights are openly available, but its terms are not an OSI-approved open-source license and include use restrictions. I've documented those terms in the repository and included Apache-2.0-licensed Qwen3 as an alternative worth exploring.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spirit of It
&lt;/h2&gt;

&lt;p&gt;Día de los Muertos isn't about declaring people gone. It's about remembering them and keeping their stories present.&lt;/p&gt;

&lt;p&gt;That's the philosophy I wanted GhostRepo to embody.&lt;/p&gt;

&lt;p&gt;A repository's last commit isn't necessarily its final chapter. There may still be useful ideas, working code, and knowledge worth preserving.&lt;/p&gt;

&lt;p&gt;An autopsy usually ends with a cause of death. GhostRepo's ends with a list of what's still there and what someone can do next.&lt;/p&gt;

&lt;p&gt;For a final test, I ran GhostRepo on itself, a project that was only hours old.&lt;/p&gt;

&lt;p&gt;The result?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Health indicator: 62&lt;/li&gt;
&lt;li&gt;Ruff issues: 0&lt;/li&gt;
&lt;li&gt;Finding: No license file detected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The missing license was intentional at that point, but the finding was correct.&lt;/p&gt;

&lt;p&gt;I liked that result. A tool that simply flatters its own creator wouldn't be much use when investigating someone else's ghosts.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sometimes resurrection starts with being honest about what's broken.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Best Use of Gemma&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Gemma 3 (&lt;code&gt;gemma3:4b&lt;/code&gt;) runs locally through Ollama and powers GhostRepo's AI analysis. Its output is validated against collected evidence, checked for unsupported claims, and kept separate from deterministic findings.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>SlothSort: A Local AI That Tidies Your Downloads Folder and Never Moves a File Without Asking</title>
      <dc:creator>SHWETANK-SAURABH</dc:creator>
      <pubDate>Sun, 25 Oct 2026 21:56:37 +0000</pubDate>
      <link>https://dev.to/shwetanksaurabh/slothsort-a-local-ai-that-tidies-your-downloads-folder-and-never-moves-a-file-without-asking-1a73</link>
      <guid>https://dev.to/shwetanksaurabh/slothsort-a-local-ai-that-tidies-your-downloads-folder-and-never-moves-a-file-without-asking-1a73</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;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;My Downloads folder had files named &lt;code&gt;doc_001.pdf&lt;/code&gt;, &lt;code&gt;scan_004.pdf&lt;/code&gt;, &lt;code&gt;IMG_8291.jpg&lt;/code&gt;, and &lt;code&gt;notes_final_v3.txt&lt;/code&gt;. Finding anything meant opening files one by one, figuring out what they contained, renaming them, and dragging them somewhere sensible.&lt;/p&gt;

&lt;p&gt;I kept putting it off because sorting files is one of those chores that never feels urgent enough to do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SlothSort does that chore for you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Point it at a folder, and SlothSort scans supported files, extracts available text, and asks an open-weight AI model running on your own machine to suggest meaningful filenames and categories. It then presents the complete plan for your review.&lt;/p&gt;

&lt;p&gt;Nothing moves until you approve it. Existing files are never intentionally overwritten or deleted, and successful moves can be undone when the original location remains available and the integrity checks pass.&lt;/p&gt;

&lt;p&gt;Here are some real suggestions from Gemma 3 4B on my synthetic demo folder:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;doc_001.pdf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Invoices/Greenfield_Power_Electricity_Bill_October_2026.pdf&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hw3.docx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Assignments/CS101_Assignment_3_Sorting_Algorithms.docx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;notes_final_v3.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Notes/Project_Kestrel_Weekly_Meeting_Notes.txt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Downloads/download (3).pdf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Receipts/Pinecone_Books_Order_Receipt_PB88213.pdf&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are proposed destinations, not hardcoded mappings. The application generates suggestions from the available file evidence, and you can review or edit them before anything changes.&lt;/p&gt;

&lt;p&gt;SlothSort is for anyone with a messy folder full of bills, receipts, coursework, screenshots, and meeting notes. It's also exactly the kind of folder you might not want to upload to a cloud AI.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Project page:&lt;/strong&gt; &lt;a href="https://slothsort-two.vercel.app" rel="noopener noreferrer"&gt;https://slothsort-two.vercel.app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SlothSort deliberately has no hosted version. It organizes files on the computer where it runs, using a model running on that same computer. A hosted copy would operate on the server's filesystem instead, or require uploading files to the server.&lt;/p&gt;

&lt;p&gt;Keeping the application local is part of the product, not an afterthought.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;GitHub repository:&lt;/strong&gt; &lt;a href="https://github.com/SHWETANK-SAURABH/slothsort" rel="noopener noreferrer"&gt;SHWETANK-SAURABH/slothsort&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clone the repository and run it locally:&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/SHWETANK-SAURABH/slothsort.git
&lt;span class="nb"&gt;cd &lt;/span&gt;slothsort
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
ollama pull gemma3:4b
&lt;span class="nv"&gt;SLOTHSORT_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gemma3:4b streamlit run app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need Python, Ollama, and enough memory to run the selected model. The initial installation and model download require internet access. Once the model is available locally, the core workflow can run without internet access.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Open-source AI at the core
&lt;/h3&gt;

&lt;p&gt;SlothSort is built around local inference rather than a proprietary cloud LLM API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Google's open-weight Gemma 3 4B, running locally through Ollama. The model is configurable, and the sidebar lets users select models available in their Ollama installation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inference:&lt;/strong&gt; Ollama's local HTTP API at &lt;code&gt;localhost:11434&lt;/code&gt;. I use Python's standard-library &lt;code&gt;urllib&lt;/code&gt;, so there's no additional SDK or API key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured output:&lt;/strong&gt; I pass a JSON Schema to Ollama's &lt;code&gt;format&lt;/code&gt; option. The model returns a category, suggested filename, explanation, and confidence score. Pydantic validates the response. Malformed responses receive a bounded retry; if that fails, the file falls back to clearly labelled rule-based suggestions and is flagged for review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything else:&lt;/strong&gt; Streamlit for the UI, PyMuPDF for PDF text extraction, Pydantic for validation, Pillow for image handling, SQLite for the action journal, and pytest for automated tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The interesting part: don't trust the model
&lt;/h3&gt;

&lt;p&gt;A small local model can read text like "ELECTRICITY BILL — Amount due: $84.20" and suggest the category &lt;code&gt;Invoices&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But a language model should never be trusted to decide which filesystem operations are safe.&lt;/p&gt;

&lt;p&gt;SlothSort separates AI reasoning from file execution:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    S[Scan files — read only] --&amp;gt; M[Gemma 3 4B via Ollama]
    M --&amp;gt;|Structured JSON| V[Deterministic validator]
    V --&amp;gt; R[User reviews and edits]
    R --&amp;gt;|Explicit approval| E[Safe file executor]
    E --&amp;gt; J[(SQLite action journal)]
    J --&amp;gt; U[Undo supported moves]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Here's how that separation works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The model only proposes.&lt;/strong&gt; It returns structured text; it cannot execute commands or modify files directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every suggestion is validated.&lt;/strong&gt; The category must be allowed, the original file extension is preserved, and unsafe names, absolute paths, path traversal, and reserved Windows filenames are rejected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destinations are constrained.&lt;/strong&gt; The resolved destination must remain inside the folder the user selected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File contents are untrusted input.&lt;/strong&gt; Extracted text is clearly delimited, and the system prompt instructs the model to treat it as data rather than instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt injection is part of the demo.&lt;/strong&gt; My synthetic dataset includes a file that tries to instruct the model to move itself into &lt;code&gt;system32&lt;/code&gt;. The model ignored it during my demo run, and the independent path validator is designed to reject an unsafe destination regardless of what the model suggests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approval is mandatory.&lt;/strong&gt; The executor accepts only an approved plan with a fingerprint. If the plan changes after approval, execution is rejected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Files are checked again before moving.&lt;/strong&gt; SlothSort rechecks file identity using size, modification time, and SHA-256, and verifies that the destination does not already exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Moves are journaled.&lt;/strong&gt; The SQLite action journal records operations so supported moves can be undone, including after an application restart. Undo also refuses to overwrite existing files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key design principle is simple: &lt;strong&gt;AI decides what might be useful; deterministic code decides what is allowed.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The bug that taught me something
&lt;/h3&gt;

&lt;p&gt;On my first real run, Gemma named a grocery receipt &lt;code&gt;Maple_Market_Receipt_September_2021.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The receipt said 2026.&lt;/p&gt;

&lt;p&gt;It also reported a confidence score of 0.95, just as it did for nearly everything else. That taught me that a model's self-reported confidence isn't a reliable way to decide whether a suggestion is trustworthy.&lt;/p&gt;

&lt;p&gt;So I added a &lt;strong&gt;grounding check&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Any number in a proposed filename — a year, invoice ID, or amount — must appear in the extracted file text or metadata. For files without readable text, such as images and scans, suggested names are restricted to generic terms and words grounded in the original filename.&lt;/p&gt;

&lt;p&gt;If a suggestion fails, SlothSort flags it for review with a message such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check name: "2021" not found in the file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's a deliberately simple check, not a complete hallucination detector. But unlike trusting a confidence score, it catches a specific, reproducible class of errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measured, not promised
&lt;/h3&gt;

&lt;p&gt;I created a 29-file synthetic demo dataset containing invoices, receipts, assignments, notes, screenshots, photos, two pairs of exact duplicates, two unsupported files, and a prompt-injection example.&lt;/p&gt;

&lt;p&gt;I manually labelled 27 files and ran the evaluation script twice.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Run 1&lt;/th&gt;
&lt;th&gt;Run 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 3 4B category accuracy&lt;/td&gt;
&lt;td&gt;25/27 (92.6%)&lt;/td&gt;
&lt;td&gt;24/27 (88.9%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filename-only baseline accuracy&lt;/td&gt;
&lt;td&gt;17/27 (63.0%)&lt;/td&gt;
&lt;td&gt;17/27 (63.0%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Malformed model responses&lt;/td&gt;
&lt;td&gt;0 of 7 calls&lt;/td&gt;
&lt;td&gt;0 of 7 calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average planning time&lt;/td&gt;
&lt;td&gt;≈3.0 s/file&lt;/td&gt;
&lt;td&gt;≈2.2 s/file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Approved moves completed / undone&lt;/td&gt;
&lt;td&gt;27/27 / 27/27&lt;/td&gt;
&lt;td&gt;27/27 / 27/27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unsafe-operation probes blocked&lt;/td&gt;
&lt;td&gt;12/12&lt;/td&gt;
&lt;td&gt;12/12&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Twenty-seven labelled files are a sanity check, not a serious benchmark. Results varied between runs even at temperature zero, and the small sample means these numbers shouldn't be generalized to arbitrary folders.&lt;/p&gt;

&lt;p&gt;The project also has 62 automated tests, with Ollama mocked so the test suite doesn't require a running model. These include a full UI click-through using Streamlit's AppTest.&lt;/p&gt;

&lt;p&gt;The numbers above describe my recorded evaluation runs; the tests and evaluation scripts are available in the repository so others can inspect and reproduce them.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the work got done
&lt;/h3&gt;

&lt;p&gt;I built SlothSort in a week with the help of an AI coding agent.&lt;/p&gt;

&lt;p&gt;I wrote the specification, including the technology stack, safety rules, priorities, and the requirement not to claim functionality before testing it. The agent wrote most of the code, ran tests, and helped with the real-model evaluation along the way.&lt;/p&gt;

&lt;p&gt;I made the architectural decisions and checked the results. The commit history documents the progression from the core engine and tests to the UI and documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;For SlothSort, open-weight AI is what makes the local-first approach practical.&lt;/p&gt;

&lt;p&gt;A file organizer needs to read your files: utility bills with account numbers, receipts, coursework with your name on it, and possibly a lease agreement.&lt;/p&gt;

&lt;p&gt;With a cloud LLM API, the excerpts needed for classification would be sent to someone else's server. You'd need an API key, an internet connection, and confidence in the provider's data-handling policies.&lt;/p&gt;

&lt;p&gt;With Gemma running locally through Ollama, file content can stay on your computer. SlothSort's model requests go to the local Ollama endpoint rather than a remote LLM service.&lt;/p&gt;

&lt;p&gt;That changes the privacy story from a promise about a provider's policies to an architectural choice that users can inspect.&lt;/p&gt;

&lt;p&gt;Open weights also provide practical advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No model-provider lock-in.&lt;/strong&gt; The model can be changed without redesigning the entire application. The evaluation script can be run again to compare supported models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No per-file API bill.&lt;/strong&gt; Once the model has been downloaded, inference uses the user's own hardware rather than charging for each API request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User control.&lt;/strong&gt; Developers can inspect the application, modify the prompts, change the model, and test the results themselves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline capability.&lt;/strong&gt; After setup and model download, the core workflow can operate without internet access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Local inference isn't free in an absolute sense: it consumes hardware resources and electricity, and larger models may require substantial memory. But it removes the need to send document contents to a proprietary inference service.&lt;/p&gt;

&lt;p&gt;A 4B open-weight model isn't necessarily as capable as the largest closed models. For a narrow task like file organization, though, pairing a local model with deterministic validation and explicit human approval makes the trade-off worthwhile.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&lt;/h2&gt;

&lt;p&gt;On my demo folder, planning 27 files took approximately a minute of model time. My job was to watch the progress bar and then review a single table rather than opening 27 files individually.&lt;/p&gt;

&lt;p&gt;I haven't timed myself organizing the same folder manually, so I won't claim a specific number of minutes saved.&lt;/p&gt;

&lt;p&gt;The bigger payoff is that I no longer have to put the task off until I have the patience to do it. SlothSort handles the repetitive work, while I retain control over the final changes.&lt;/p&gt;

&lt;p&gt;That's the point of Slow and Steady: let the machine handle the boring part, and spend your attention where it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; SlothSort uses Google's open-weight Gemma 3 4B locally through Ollama. The reported model-evaluation results above come from Gemma.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Built with local AI, deterministic safety checks, and a healthy respect for the art of doing less repetitive work.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>ReceiptPilot: a local-first AI receipt assistant for freelancers</title>
      <dc:creator>SHWETANK-SAURABH</dc:creator>
      <pubDate>Sun, 18 Oct 2026 22:24:13 +0000</pubDate>
      <link>https://dev.to/shwetanksaurabh/receiptpilot-a-local-first-ai-receipt-assistant-for-freelancers-4gop</link>
      <guid>https://dev.to/shwetanksaurabh/receiptpilot-a-local-first-ai-receipt-assistant-for-freelancers-4gop</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Every freelancer I know keeps the same folder: phone photos of taxi receipts, PDF invoices from software vendors, and hotel bills scanned at an angle. Once a month, or worse, once a year at tax time, someone types all of it into a spreadsheet. Merchant, date, currency, subtotal, tax, total. One receipt at a time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ReceiptPilot&lt;/strong&gt; does that typing. You upload JPG, PNG, or PDF files. An open-weight vision model can read them locally on your own computer. Ordinary code checks the numbers. Then you review every field next to the original document before it becomes a confirmed expense. Confirmed expenses go into a ledger you can filter and export as CSV for your accountant.&lt;/p&gt;

&lt;p&gt;The customer is deliberately narrow: a freelancer or two-person business handling somewhere between 20 and 200 receipts a month, who enters them by hand today and doesn't want to send financial documents to another cloud service. The value is less repetitive data entry, fewer manual transcription errors, and more control over sensitive documents.&lt;/p&gt;

&lt;p&gt;I can see two ways to charge for it: a paid desktop bundle with the model preconfigured, or a managed installation that an accountant rolls out to their freelance clients.&lt;/p&gt;

&lt;p&gt;What it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reads documents with a local open-weight model:&lt;/strong&gt; Gemma 3 4B through Ollama. Text-based PDFs are read from their embedded text; photos and scans are processed visually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checks arithmetic in code, not in the prompt.&lt;/strong&gt; Amounts are parsed as exact decimals, with both &lt;code&gt;1.234,56&lt;/code&gt; and &lt;code&gt;1,234.56&lt;/code&gt; supported, and subtotal plus tax is compared with the total.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refuses to guess.&lt;/strong&gt; A date like &lt;code&gt;03/04/2026&lt;/code&gt; or a bare &lt;code&gt;$&lt;/code&gt; is flagged for you to resolve instead of being silently interpreted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Catches potential duplicates.&lt;/strong&gt; It checks identical files, matching invoice numbers, and combinations of total, date proximity, and similar merchant names. It gives you a score and a plain-English reason. You decide what to do; nothing is automatically deleted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shows where every value came from:&lt;/strong&gt; extracted from the document, suggested, or edited by you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handles extraction failures gracefully.&lt;/strong&gt; If extraction fails, the receipt can still be saved so you can retry or enter the details manually.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Try ReceiptPilot:&lt;/strong&gt; &lt;a href="https://receiptpilot-eight.vercel.app/" rel="noopener noreferrer"&gt;Open the live application&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/SHWETANK-SAURABH/ReceiptPilot" rel="noopener noreferrer"&gt;GitHub — SHWETANK-SAURABH/ReceiptPilot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The live web application provides a way to explore the interface. The local setup below is the intended way to run the complete application with your own Ollama model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdplpm44anoz39jyotiwt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdplpm44anoz39jyotiwt.png" alt="Demo" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dashboard keeps confirmed totals separate by currency:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiwgqw9ftdrqeispl67gl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiwgqw9ftdrqeispl67gl.png" alt="The dashboard keeps confirmed totals separate by currency:" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ledger supports filtering and CSV export:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffbj7f3q7i3tf5lgnxwmo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffbj7f3q7i3tf5lgnxwmo.png" alt="The ledger supports filtering and CSV export:" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To run the application locally, install &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, ensure Docker is installed, and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma3:4b
docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The complete setup instructions and configuration are available in the &lt;a href="https://github.com/SHWETANK-SAURABH/ReceiptPilot" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/SHWETANK-SAURABH/ReceiptPilot" rel="noopener noreferrer"&gt;ReceiptPilot on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project is MIT licensed and uses a Next.js 16 frontend, a FastAPI backend, and SQLite or PostgreSQL for storage. The repository includes backend and frontend tests, Playwright end-to-end tests, Docker Compose configuration, and a Vercel configuration.&lt;/p&gt;

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



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    U([User]) --&amp;gt; FE["Next.js web app"]
    FE --&amp;gt; API["FastAPI"]
    API --&amp;gt; P["PDF text extraction / page rendering&amp;lt;br/&amp;gt;Image preprocessing"]
    P --&amp;gt; M["Ollama&amp;lt;br/&amp;gt;Gemma 3 4B"]
    M --&amp;gt; V["Code validation&amp;lt;br/&amp;gt;Decimals, dates, arithmetic"]
    V --&amp;gt; D["Duplicate detection"]
    D --&amp;gt; DB[("Database")]
    DB --&amp;gt; FE&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;
  
  
  The open-weight models
&lt;/h3&gt;

&lt;p&gt;The default model is &lt;code&gt;gemma3:4b&lt;/code&gt;, served through Ollama. I developed and tested the application on a laptop with an NVIDIA RTX 4060 GPU with 8 GB of VRAM.&lt;/p&gt;

&lt;p&gt;I also compared it with &lt;code&gt;qwen2.5vl:7b&lt;/code&gt;, which uses the Apache 2.0 license. In my 40-document synthetic evaluation, Qwen was slightly more accurate, while Gemma was faster and required fewer resources.&lt;/p&gt;

&lt;p&gt;The measured median processing times were 4.6 seconds per document for Gemma and 6.4 seconds for Qwen. These figures describe my test environment, not a guaranteed performance level on every machine.&lt;/p&gt;

&lt;p&gt;Gemma is the default because the goal is to make the application practical on more modest hardware. Qwen remains an alternative. The model interface is separated from the business logic, so swapping models or inference runtimes does not require rewriting the rest of the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  The design decision that mattered most: the model reads, the code decides
&lt;/h3&gt;

&lt;p&gt;Ollama's structured output constrains the model to a JSON schema in which fields are allowed to be empty. The model does not have to invent a value just because a field exists.&lt;/p&gt;

&lt;p&gt;After extraction, deterministic Python code validates the response. It parses amounts as decimals, checks totals, identifies ambiguous dates, and looks for potential duplicates.&lt;/p&gt;

&lt;p&gt;Nothing is automatically changed to make the numbers add up. If a total does not match, the application shows a warning and lets the user decide what to do.&lt;/p&gt;

&lt;p&gt;That decision mattered during development. In an early test, the model returned a line item as &lt;code&gt;2 × 9.98 = 9.98&lt;/code&gt;. The arithmetic validation caught the inconsistency.&lt;/p&gt;

&lt;p&gt;Another model run showed Gemma copying line totals into unit-price fields when a receipt printed only one amount per line. This produced warnings on records that were actually correct, which could eventually train users to ignore warnings.&lt;/p&gt;

&lt;p&gt;I revised the prompt and reran the evaluation for both models. The revised evaluation no longer showed that particular source of warning noise. This was a useful reminder that validation logic is only as useful as the quality of the data it validates and the warnings it produces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measuring it honestly
&lt;/h3&gt;

&lt;p&gt;I created a labelled set of 40 synthetic documents, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear receipts and invoices.&lt;/li&gt;
&lt;li&gt;Text-based and scanned PDFs, including multi-page documents.&lt;/li&gt;
&lt;li&gt;Blurred, skewed, rotated, dark, faded, and low-resolution images.&lt;/li&gt;
&lt;li&gt;Missing fields and ambiguous dates.&lt;/li&gt;
&lt;li&gt;Planted arithmetic errors.&lt;/li&gt;
&lt;li&gt;True duplicates and similar-looking documents that were not duplicates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every document went through the actual extraction and validation pipeline.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluation metric&lt;/th&gt;
&lt;th&gt;Gemma 3 4B&lt;/th&gt;
&lt;th&gt;Qwen2.5-VL 7B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Documents with every field correct&lt;/td&gt;
&lt;td&gt;38/40&lt;/td&gt;
&lt;td&gt;39/40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Values invented where a field should be empty&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Planted total errors caught&lt;/td&gt;
&lt;td&gt;2/2&lt;/td&gt;
&lt;td&gt;2/2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;False alarms on planted total-error checks&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ambiguous dates correctly left for the user&lt;/td&gt;
&lt;td&gt;4/4&lt;/td&gt;
&lt;td&gt;4/4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicates detected&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Look-alikes incorrectly flagged as duplicates&lt;/td&gt;
&lt;td&gt;0/4&lt;/td&gt;
&lt;td&gt;0/4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Median processing time per document&lt;/td&gt;
&lt;td&gt;4.6 s&lt;/td&gt;
&lt;td&gt;6.4 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The remaining extraction misses were fields left blank rather than incorrect values. The application marks these fields as missing instead of silently filling them with guesses.&lt;/p&gt;

&lt;p&gt;There are important limitations. These are synthetic documents, the sample contains only 40 documents, and it does not represent the full variety of real-world receipts. Crumpled paper, unusual layouts, handwritten notes, unfamiliar currencies, and poor scans may produce worse results.&lt;/p&gt;

&lt;p&gt;These numbers should be treated as an initial engineering evaluation, not a claim of production-grade accuracy. The dataset, evaluation runner, and raw results are available in the repository so that others can reproduce or extend the tests.&lt;/p&gt;

&lt;p&gt;The evaluation also caught errors in my own ground-truth labels. Two labels needed correction because dates such as &lt;code&gt;01-10-2026&lt;/code&gt; are ambiguous without an agreed date convention. The application had correctly refused to choose between 1 October and 10 January. I corrected the labels and documented the change rather than silently adjusting the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Receipts can contain names, partial card details, addresses, and a detailed record of how someone spends their money.&lt;/p&gt;

&lt;p&gt;A receipt-processing service built entirely around a closed API can require users to send financial documents to an external provider. With open weights and local inference, ReceiptPilot can run its extraction pipeline on a user's own machine without sending documents to an external AI API.&lt;/p&gt;

&lt;p&gt;That makes local operation possible without a per-page AI API fee. It also gives users more control over where their documents are processed.&lt;/p&gt;

&lt;p&gt;Open weights made experimentation practical, too. I could run two models on the same dataset, compare their results, and choose a model based on accuracy, speed, and hardware requirements instead of relying on a single provider.&lt;/p&gt;

&lt;p&gt;Privacy still depends on configuration. If ReceiptPilot is pointed at a remote Ollama server, documents sent for inference may leave the user's device. A local model alone does not guarantee that every part of a web application is private.&lt;/p&gt;

&lt;p&gt;That is why the distinction between a hosted interface and a fully local deployment matters. Users should understand where documents are stored, which server processes them, and whether data leaves their device.&lt;/p&gt;

&lt;p&gt;For this project, open innovation is not just about avoiding a proprietary API. It makes model choice, reproducibility, and local-first deployment practical design options.&lt;/p&gt;

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

&lt;p&gt;The next step is validation with real freelancers: processing their own receipts with permission, measuring the time saved, and publishing the results alongside the synthetic evaluation.&lt;/p&gt;

&lt;p&gt;After that, I want to work on a one-click desktop build, support tips, fees, and discounts as proper fields, and add export formats for accounting tools such as Xero and QuickBooks.&lt;/p&gt;

&lt;p&gt;ReceiptPilot is an attempt to make a small but tedious task easier without forcing users to give up control of their financial documents. There is still work to do, especially around real-world accuracy and usability, but the core workflow is in place.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>TouchGrass: a phone referee for outdoor games, with an open-weight Game Master</title>
      <dc:creator>SHWETANK-SAURABH</dc:creator>
      <pubDate>Sun, 11 Oct 2026 21:32:13 +0000</pubDate>
      <link>https://dev.to/shwetanksaurabh/touchgrass-a-phone-referee-for-outdoor-games-with-an-open-weight-game-master-1ik6</link>
      <guid>https://dev.to/shwetanksaurabh/touchgrass-a-phone-referee-for-outdoor-games-with-an-open-weight-game-master-1ik6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;TouchGrass&lt;/strong&gt; is a referee for outdoor games that lives on your phones, so nobody has to stay behind to keep score.&lt;/p&gt;

&lt;p&gt;The idea is simple: everyone should be able to play instead of arguing about the rules, keeping score, or watching the clock. TouchGrass is designed to handle those jobs so friends, families, and youth groups can spend more time playing outside.&lt;/p&gt;

&lt;p&gt;The project has a game engine, a multiplayer server, persistent game sessions, and an AI Game Master powered by an open weight model.&lt;/p&gt;

&lt;p&gt;The Game Master can propose a twist for a round: a safe zone rule, a bonus challenge, or a little more time. But there is one important distinction: &lt;strong&gt;the AI proposes; the game engine decides.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The model never directly changes the game state. Its proposal must pass three checks implemented in ordinary code before it can be applied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gemma proposes
      ↓
Schema validation
      ↓
Game legality check
      ↓
Deterministic safety validation
      ↓
Game engine applies the event
      ↓
Players receive the update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the model is slow, produces malformed output, proposes an illegal event, or is switched off, the game can continue without that AI event.&lt;/p&gt;

&lt;p&gt;The project is intended for friends, families, and youth groups who want a reason to go to the park, and for anyone who has ever argued about whether a tag counted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What works today:&lt;/strong&gt; Tag and Hide &amp;amp; Seek are playable for 2–4 players, with three rounds and a named winner. Three other games Ice &amp;amp; Fire, Treasure Hunt, and Nature Challenge have their rules defined but are not playable yet. The website labels them as coming soon rather than pretending they are finished.&lt;/p&gt;

&lt;p&gt;The browser-based gameplay client is not implemented yet. The game flow has been tested locally through the game server.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Live website:&lt;/strong&gt; &lt;a href="https://touchgrass-game-master.vercel.app/" rel="noopener noreferrer"&gt;https://touchgrass-game-master.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The live website presents the project, games, rules, and design. It does not yet support playing a complete game directly in the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local demo:&lt;/strong&gt; The game server has been tested locally with multiple players and a real Game Master turn. The recording below demonstrates session creation, player joining, a round starting, and a Game Master event being applied after validation.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/5DHvokMNDh4" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;GitHub repository:&lt;/strong&gt; &lt;a href="https://github.com/SHWETANK-SAURABH/touchgrass_game_master" rel="noopener noreferrer"&gt;https://github.com/SHWETANK-SAURABH/touchgrass_game_master&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project is MIT licensed. The README contains the commands needed to install dependencies, run verification, start the server, and try the local demo.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  The open-source AI at the centre
&lt;/h3&gt;

&lt;p&gt;The Game Master uses three important pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 3 (4B):&lt;/strong&gt; An open weight model served locally through Ollama.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mastra:&lt;/strong&gt; An open-source agent framework used to structure the Game Master agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A replaceable model provider:&lt;/strong&gt; The application can use Ollama or a compatible model endpoint selected through configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my local setup, Gemma runs on my own machine. No hosted AI API is required for a Game Master turn in that configuration.&lt;/p&gt;

&lt;p&gt;One rule shaped the entire architecture:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The AI proposes. The safety layer validates. The game engine executes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A language model can be wrong, slow, or creative in ways that are inappropriate for an outdoor game. It should not be responsible for deciding whether an action is legal or whether the game state is valid.&lt;/p&gt;

&lt;p&gt;Instead, TouchGrass uses a deterministic game engine. Game events are checked against the rules, and the same valid event sequence produces the same game-state transitions. That makes the core mechanics testable independently of the model.&lt;/p&gt;

&lt;p&gt;The model is treated as an untrusted source of suggestions, not as the authority over the game.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens during a Game Master turn?
&lt;/h3&gt;

&lt;p&gt;Each turn follows a controlled pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The agent receives game context.&lt;/strong&gt; It sees a trimmed view of the current game rather than unrestricted internal state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It can use two read-only tools.&lt;/strong&gt; &lt;code&gt;game_history&lt;/code&gt; retrieves historical game records, while &lt;code&gt;game_rules&lt;/code&gt; provides information about the rules and constraints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It returns a structured proposal.&lt;/strong&gt; The response must match the expected schema, which is validated with Zod.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The engine checks legality.&lt;/strong&gt; An event that is not valid for the current game state is rejected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The deterministic safety layer checks the proposal.&lt;/strong&gt; It rejects recognised unsafe event types and options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The engine applies accepted events.&lt;/strong&gt; Only validated events can mutate the game state, after which the update is broadcast and persisted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tool calls cannot directly mutate game state. Unknown tools are rejected, and tool inputs and outputs are bounded.&lt;/p&gt;

&lt;p&gt;If the model produces malformed output or the proposal fails validation, the game does not apply the invalid event.&lt;/p&gt;

&lt;p&gt;This design lets the AI contribute creativity without giving it control over the rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  The rest of the stack
&lt;/h3&gt;

&lt;p&gt;The project uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript in a monorepo&lt;/li&gt;
&lt;li&gt;Next.js 16 for the website&lt;/li&gt;
&lt;li&gt;A WebSocket game server for multiplayer sessions&lt;/li&gt;
&lt;li&gt;MongoDB for persisted game sessions&lt;/li&gt;
&lt;li&gt;Mastra for the Game Master agent&lt;/li&gt;
&lt;li&gt;Gemma 3 (4B) through Ollama for local inference&lt;/li&gt;
&lt;li&gt;Zod for structured-output validation&lt;/li&gt;
&lt;li&gt;Sentry integration and application-level observability&lt;/li&gt;
&lt;li&gt;An automated evaluation suite for Game Master behaviour&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository has more than 2,000 automated tests, including 35 scripted Game Master evaluation scenarios. The goal is to make changes measurable rather than relying only on whether a prompt sounds better.&lt;/p&gt;

&lt;p&gt;The production server currently uses a single process multiplayer architecture. It is not designed for multiple independent game server instances sharing the same database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Things I learned the honest way
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The model saw something it should not have.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;During development, a game join code leaked into the model's view through a historical game record. The model did not need that information. I traced the issue to the history view and corrected the redaction.&lt;/p&gt;

&lt;p&gt;That experience reinforced an important lesson: limiting the model's authority is not enough. The information it receives must also be limited to what it needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Small models can be cautious.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my tests with Gemma 3 (4B), the Game Master frequently chose to extend the round. That is a legal and safe option, but repeated use makes the game less varied.&lt;/p&gt;

&lt;p&gt;A smaller model can be useful for a controlled task, but its behaviour still needs evaluation. The next challenge is to encourage more variety without weakening the validation rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Safety rules have limits.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The safety validator is a set of deterministic rules. It can reject hazards that it recognises, but it does not know the park where players are standing, the traffic nearby, or the actual conditions around them.&lt;/p&gt;

&lt;p&gt;It does not make outdoor play automatically safe. Players still need to use their judgement and follow local safety rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. A successful model response is not the same as a successful game event.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The model can return valid JSON and still suggest something that is illegal in the current game. Structured output solves only one part of the problem.&lt;/p&gt;

&lt;p&gt;Keeping schema validation, engine legality, and safety validation separate made those failure cases easier to test and reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Three things became easier because I could run and inspect an open-weight model.&lt;/p&gt;

&lt;h3&gt;
  
  
  I could inspect the model's inputs and outputs
&lt;/h3&gt;

&lt;p&gt;Because Gemma runs locally through Ollama in my development setup, I can inspect what goes into the model and what comes back.&lt;/p&gt;

&lt;p&gt;That helped me discover the join-code leak and correct the model's input.&lt;/p&gt;

&lt;p&gt;Open weights do not automatically guarantee privacy or security, but they give developers more control over the inference environment and make it easier to investigate model behaviour.&lt;/p&gt;

&lt;h3&gt;
  
  
  The evaluation suite gives me a repeatable baseline
&lt;/h3&gt;

&lt;p&gt;The Game Master evaluation suite contains 35 scripted scenarios covering expected outcomes and failure stages.&lt;/p&gt;

&lt;p&gt;Running these tests against a fixed model version makes comparisons more meaningful when I change prompts, tool descriptions, or safety rules. It does not eliminate model variability, but it gives me a repeatable baseline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local inference avoids per-turn hosted AI charges
&lt;/h3&gt;

&lt;p&gt;In my local configuration, the Game Master runs through Ollama without making a per-turn call to a hosted AI provider.&lt;/p&gt;

&lt;p&gt;That can make experimentation more accessible to developers, families, and youth groups who do not want a usage-based AI bill.&lt;/p&gt;

&lt;p&gt;It is important to distinguish local inference from a completely offline application: dependencies and model weights must be installed first, and other services may still need network access.&lt;/p&gt;

&lt;p&gt;The broader benefit is freedom to experiment. Someone else can add a game, replace the model, improve the evaluation suite, or translate the safety rules without needing permission from a closed API provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Best Use of Gemma
&lt;/h3&gt;

&lt;p&gt;Gemma 3 (4B) acts as the Game Master. Through Ollama, it receives game context, can consult read-only tools, and proposes a single game twist as structured JSON.&lt;/p&gt;

&lt;p&gt;The architecture is built around what a small open-weight model can contribute ideas while leaving legality and execution to deterministic code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Use of Mastra
&lt;/h3&gt;

&lt;p&gt;The Game Master agent is built using Mastra. Its two read-only tools, &lt;code&gt;game_history&lt;/code&gt; and &lt;code&gt;game_rules&lt;/code&gt;, provide additional context before it proposes an event.&lt;/p&gt;

&lt;p&gt;The application does not give the agent unrestricted control over the game. Its output must pass the existing schema, engine-legality, and safety checks before any change is applied.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;The immediate priorities are to build the browser-based gameplay client, make the remaining three games playable, and improve the variety of Game Master events without compromising the safety boundary.&lt;/p&gt;

&lt;p&gt;I also want to make the project easier for open-source contributors to extend through better game definitions, more evaluation scenarios, accessibility improvements, and localisation.&lt;/p&gt;

&lt;p&gt;I built TouchGrass with help from an AI coding assistant, including assistance with implementation and this article. The architectural decisions, testing, and decisions about what the project can honestly claim remain my responsibility.&lt;/p&gt;

&lt;p&gt;The central idea is simple: use open-weight AI to make games more interesting, but never let the model become the referee.&lt;/p&gt;

&lt;p&gt;Let the model suggest. Let code check. Let everyone play.&lt;/p&gt;

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