<?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: Abhinav Sankarshana Dasu</title>
    <description>The latest articles on DEV Community by Abhinav Sankarshana Dasu (@abhinav412).</description>
    <link>https://dev.to/abhinav412</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%2F4154542%2F1cfba8b5-cccd-4475-b93e-b3bc602096c1.png</url>
      <title>DEV Community: Abhinav Sankarshana Dasu</title>
      <link>https://dev.to/abhinav412</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhinav412"/>
    <language>en</language>
    <item>
      <title>sloth-tidy: the file organizer that files like you — and never repeats a mistake</title>
      <dc:creator>Abhinav Sankarshana Dasu</dc:creator>
      <pubDate>Thu, 22 Oct 2026 17:55:13 +0000</pubDate>
      <link>https://dev.to/abhinav412/sloth-tidy-the-file-organizer-that-files-like-you-and-never-repeats-a-mistake-1mgi</link>
      <guid>https://dev.to/abhinav412/sloth-tidy-the-file-organizer-that-files-like-you-and-never-repeats-a-mistake-1mgi</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;h1&gt;
  
  
  What I Built
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;sloth-tidy&lt;/strong&gt; — a local file organizer that tidies a messy folder &lt;em&gt;overnight&lt;/em&gt;, files it the way &lt;strong&gt;you&lt;/strong&gt; file it (not the way a generic tool guesses), and gets better every time you tell it it's wrong.&lt;/p&gt;

&lt;p&gt;It is deliberately not the first "point a local LLM at Downloads" tool. Plenty shipped this month (&lt;a class="mentioned-user" href="https://dev.to/tahary"&gt;@tahary&lt;/a&gt;'s daemon-style SlothTidy, &lt;a class="mentioned-user" href="https://dev.to/sufian_khan"&gt;@sufian_khan&lt;/a&gt;'s Messimo, and more). In every one of them the same failure shows up after a few nights: the model files your invoices the way an &lt;em&gt;average&lt;/em&gt; user would, and it commits the same mistake on night 14 that it made on night 1. You unfile it, life moves on, the tool learns nothing.&lt;/p&gt;

&lt;p&gt;sloth-tidy is built around three mechanisms that close that loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Habit profiling.&lt;/strong&gt; Before classifying anything, a read-only scanner walks the folders you &lt;em&gt;already organize yourself&lt;/em&gt; — &lt;code&gt;~/Documents&lt;/code&gt;, whatever you keep tidy — and learns your taxonomy, your naming style, and your filename habits. If your folder is called &lt;code&gt;Work &amp;amp; Career&lt;/code&gt;, the model is never even &lt;em&gt;offered&lt;/em&gt; the choice &lt;code&gt;Work&lt;/code&gt;; the allowed enum in the prompt is built from your real folder names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Undo-as-feedback.&lt;/strong&gt; &lt;code&gt;sloth-tidy undo&lt;/code&gt; doesn't just reverse a run — every reversal is recorded as a &lt;em&gt;correction&lt;/em&gt;, and the next run's classify prompt carries your recent corrections: "the user rejected this filing; do not repeat it." Fix it once, never again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-model consensus.&lt;/strong&gt; Any file the primary model (&lt;code&gt;gemma3:4b&lt;/code&gt;) tags below 0.7 confidence also gets a second opinion (&lt;code&gt;qwen2.5:3b&lt;/code&gt;). Agree → file it with the weaker vote's confidence. Disagree → one richer reconsider pass with both opinions in context; still shaky → honest &lt;code&gt;Unsorted&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And because this thing is going to move files while you sleep, safety isn't an afterthought — it's the architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dry-run default.&lt;/strong&gt; Nothing moves unless you explicitly pass &lt;code&gt;--apply&lt;/code&gt;. The preview is computed by the exact same destination math as apply, so the table you approve is what happens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Move-only.&lt;/strong&gt; &lt;code&gt;shutil.move&lt;/code&gt; is the only filesystem verb in the codebase. No delete, no overwrite, anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Journal-first.&lt;/strong&gt; Every move is recorded &lt;code&gt;pending&lt;/code&gt; in an SQLite journal &lt;em&gt;before&lt;/em&gt; the move and marked &lt;code&gt;done&lt;/code&gt; (with the final destination) after. A crash can only leave a pending row — undo refuses those.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never overwrites.&lt;/strong&gt; Collisions get &lt;code&gt;-2&lt;/code&gt;, &lt;code&gt;-3&lt;/code&gt;, … suffixes — during apply, &lt;em&gt;and&lt;/em&gt; during undo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Undo forever.&lt;/strong&gt; &lt;code&gt;sloth-tidy undo&lt;/code&gt; reverses the last run (or any run by ID) newest-first. A vanished destination is logged honestly as &lt;code&gt;skipped_missing&lt;/code&gt;; a re-filled source spot gets the file restored &lt;em&gt;beside&lt;/em&gt; it, never through it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Code
&lt;/h1&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/Abhinav412/sloth-tidy" rel="noopener noreferrer"&gt;https://github.com/Abhinav412/sloth-tidy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fastest repro is three commands — a generated messy folder, previewed, then applied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python tests/make_samples.py demo/messy-inbox   &lt;span class="c"&gt;# 16 messy files incl. a dupe pair&lt;/span&gt;
sloth-tidy run &lt;span class="nt"&gt;--config&lt;/span&gt; demo/config.yaml        &lt;span class="c"&gt;# dry-run plan table — nothing moved&lt;/span&gt;
sloth-tidy run &lt;span class="nt"&gt;--config&lt;/span&gt; demo/config.yaml &lt;span class="nt"&gt;--apply&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;…and &lt;code&gt;sloth-tidy digest&lt;/code&gt; in the morning, &lt;code&gt;sloth-tidy undo&lt;/code&gt; when the weekend-you disagrees with the overnight-you (that disagreement becomes the training data the next run consumes).&lt;/p&gt;

&lt;p&gt;The part I'm most convinced by is the journal contract. A planned move is two-phase:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# mover.py — journal-first move: pending BEFORE the filesystem feels it
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_meta&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decisions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;planned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;target_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;move_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;journal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_pending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;planned&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# ... then the move, then mark_done(move_id, final_dst)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Undo lives on the other side of the same coin, and writes the correction&lt;br&gt;
memory the next run consumes:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# mover.py — undo_run: never combine a restoration and a lesson
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;moves&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                                   &lt;span class="c1"&gt;# newest-first
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;journal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_skipped_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# honest, not a crash
&lt;/span&gt;        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="n"&gt;restored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;unique_destination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# src free -&amp;gt; src, occupied -&amp;gt; src-2
&lt;/span&gt;    &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;restored&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;journal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_undone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;journal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_correction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;restored&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The category enum is enforced at inference time with Ollama's &lt;code&gt;format=&amp;lt;json&lt;br&gt;
schema&amp;gt;&lt;/code&gt; — schema-constrained decoding, not "please return JSON" hoping:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# classify.py — the model physically cannot invent a category
&lt;/span&gt;&lt;span class="n"&gt;category_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;category_schema&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;category_schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;enum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Safety has its own test file (&lt;code&gt;tests/test_safety.py&lt;/code&gt;): never-overwrite, journal&lt;br&gt;
round-trip, undo reversibility, corrections dedupe, consensus ladders.&lt;/p&gt;
&lt;h1&gt;
  
  
  How I Built It
&lt;/h1&gt;

&lt;p&gt;Everything runs on &lt;strong&gt;open-weight models through &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;&lt;/strong&gt;,&lt;br&gt;
on your laptop, with design choices tuned for small open-weight models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema-constrained prompts.&lt;/strong&gt; The pydantic &lt;code&gt;Decision&lt;/code&gt; model (flat, 4 fields —
category, new_name, summary, confidence) is compiled to a JSON schema with your
category enum injected, passed as Ollama's &lt;code&gt;format&lt;/code&gt;. Temperature 0. Invalid or
off-enum output gets exactly one corrective retry that carries the real
validation error, then a deterministic &lt;code&gt;Unsorted&lt;/code&gt; fallback. Hard cap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First profile, then classify.&lt;/strong&gt; &lt;code&gt;sloth-tidy profile&lt;/code&gt; walks your organized
directories once, caches a &lt;code&gt;HabitProfile&lt;/code&gt; (categories, naming examples, style
rules), and it merges into the prompt ahead of any manual &lt;code&gt;config.yaml&lt;/code&gt; list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consensus ladder, bounded.&lt;/strong&gt; File → model A. If A is confident, no second
call — obvious files shouldn't pay for two votes. Anywhere else, model B votes;
agreement takes the minimum confidence (honest number), disagreement triggers
one reconsider pass with both opinions in context, and a persistent
disagreement lands in &lt;code&gt;Unsorted/&lt;/code&gt; — original filename untouched, because if we
don't trust the category we shouldn't trust the invented name either.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Digest from journal, not from disk.&lt;/strong&gt; The 2-line morning summary is built
from what the journal &lt;em&gt;recorded&lt;/em&gt;, so what you read is exactly what happened —
even if users touched files since.&lt;/li&gt;
&lt;li&gt;No surprises in memory: corrections dedupe by &lt;code&gt;(src, dst, wrong_category)&lt;/code&gt;,
so repetitive undo cycles never balloon the prompt.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h1&gt;

&lt;p&gt;For this project, open is the whole trust story:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nothing leaves the laptop.&lt;/strong&gt; File contents — invoices, contracts, personal
scans — are snippets sent to a localhost daemon. A cloud API means shipping
your private papers to someone else's quota-policy. Here there is no network
path at all in the classification loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run forever.&lt;/strong&gt; An overnight organizer only creates
change if you leave it running — with 4B/3B open models on Ollama, that's
electricity, not a subscription.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The habit profile is the product, and it's only possible locally.&lt;/strong&gt; A closed
service can't build "your" taxonomy, because they'd have to walk your folders.
Mine reads them once, per cache day, and keeps the walk fully offline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swappable models.&lt;/strong&gt; The config lists models, not an account: put any two
local models in &lt;code&gt;models:&lt;/code&gt; — tonight's consensus pair changes with two lines.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  My Agent Session
&lt;/h1&gt;

&lt;p&gt;The full three-day build — design decisions, the journal-first safety contract,&lt;br&gt;
the consensus ladder, every test fix — is saved and embeddable:&lt;/p&gt;


&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="OpenCode"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;sloth-tidy: building a habit-mirroring local file organizer (3-day build)&lt;/span&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-scroll"&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Build the sloth-tidy project per ARCHITECTURE.md: a local Ollama-powered overnight file organizer with habit-mirroring, corrections memory, and two-model consensus for the Hacktoberfest Week 3 'Slow and Steady' challenge (due Oct 25).&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Research note: the Week 3 field already has 15+ entries including a same-named SlothTidy daemon. The open lane is habit-mirroring: profile what folders/naming the user already maintains, feed that into the classify prompt, and make every wrong filing a correction the model never repeats.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-writemodelspy"&gt;write models.py&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;pydantic contracts: Decision (flat 4 fields = the Ollama JSON schema), FileIn...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;pydantic contracts: Decision (flat 4 fields = the Ollama JSON schema), FileInfo, HabitProfile, Correction&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;created; validators inject allowed_categories via context&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-writeprofilerpy"&gt;write profiler.py&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;read-only walk of the user's organized folders -&amp;gt; categories, naming examples...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;read-only walk of the user's organized folders -&amp;gt; categories, naming examples, style summary, JSON cache&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;created; noise/Windows filters included&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-writescannerpy"&gt;write scanner.py&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;top-level scan with skip rules + mtime-ordered SHA-256 dupe pairing&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;top-level scan with skip rules + mtime-ordered SHA-256 dupe pairing&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;created; 1 partial download and 2-dupe pair detected on demo folder&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-writeclassifypy"&gt;write classify.py&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;schema-constrained Ollama call + bounded 1-retry ladder + deterministic Unsor...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;schema-constrained Ollama call + bounded 1-retry ladder + deterministic Unsorted fallback&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;created; category enum injected into Ollama format schema&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-shellpytesttests"&gt;shell pytest tests/&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;python -m pytest tests/&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;python -m pytest tests/&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;42 passed (Day 1 suite green)&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;go&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Day 2 = the safety layer. Journal-first contract: every planned move is recorded 'pending' in SQLite BEFORE the filesystem sees it, marked 'done' after. Undo only touches 'done' rows, newest-first.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-writejournalpy"&gt;write journal.py&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;runs/moves/corrections tables + WAL, two-phase status marks&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;runs/moves/corrections tables + WAL, two-phase status marks&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;created&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-writemoverpy"&gt;write mover.py&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;shared target math for preview AND apply, unique_destination -2/-3 suffixes, ...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;shared target math for preview AND apply, unique_destination -2/-3 suffixes, apply_plan journal-first, no overwrite ever&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;created; broken-symlink case handled via lexists&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-editclassifypy"&gt;edit classify.py&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;two-model consensus: solo when A confident, B votes below 0.7, disagreement -...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;two-model consensus: solo when A confident, B votes below 0.7, disagreement -&amp;gt; one reconsider pass, dead-B degrades to solo-A&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;edited; consensus_min wired from settings&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-writedigestpy"&gt;write digest.py&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;2-line digest + move table built from journal truth, not from disk&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;2-line digest + move table built from journal truth, not from disk&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;created; write_digest lands in dest/digest.md&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-shellpytesttests"&gt;shell pytest tests/&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;python -m pytest tests/&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;python -m pytest tests/&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;61 passed, exit 0 — safety suite covers never-overwrite, journal roundtrip, undo reversibility, corrections dedupe, consensus paths&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-shellcliliveloop"&gt;shell CLI live loop&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;run (dry) -&amp;gt; run --apply -&amp;gt; undo -&amp;gt; digest on demo folder&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;run (dry) -&amp;gt; run --apply -&amp;gt; undo -&amp;gt; digest on demo folder&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;14 files journaled run 1; undo restored 14/14 byte-identical; 16 corrections written; digest reprinted&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Found and fixed during the loop: dead model B initially punished the file with a fabricated disagreement — now B's death degrades to solo-with-A. Undo consolidated into mover.undo_run so CLI and tests share one code path.&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;go ahead with day 3&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Day 3: README (lead with habit-mirroring + corrections memory, not file-tidying), MIT license, .gitignore, demo walkthrough, and the #hf26challenge submission draft following the challenge template.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-writereadmemd"&gt;write README.md&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;invariant table (dry-run default, move-only, journal-first, never-overwrite, ...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;invariant table (dry-run default, move-only, journal-first, never-overwrite, undo forever), scheduling via schtasks + cron, honest limitations&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;created; config defaults verified against code&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-gatewaycreatearticle"&gt;gateway create_article&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;DEV draft staged (not published), tags devchallenge/hf26challenge/python/ai&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;DEV draft staged (not published), tags devchallenge/hf26challenge/python/ai&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;draft id 4898757 staged; unterminated HTML comment fixed via update_article&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;repo url set, publish the draft along with saving the agent session&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Submitting this session to DEV, embedding it into the draft's 'My Agent Session' section, and publishing on the user's explicit go-ahead.&lt;/p&gt;

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

  &lt;div class="agent-session-footer"&gt;
    &lt;span class="agent-session-meta"&gt;
        11 of 13 messages
          · some content was hidden by the author
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;



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

&lt;p&gt;The stall: my Downloads folder is the eternal triage list — every few days, ten&lt;br&gt;
minutes of poking, re-filing and "where does this even belong". The payoff:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;~16 files nightly, and after two undo cycles the same mistake never comes back&lt;/li&gt;
&lt;li&gt;One command, scheduled, no UI, no paid API&lt;/li&gt;
&lt;li&gt;The real payoff is qualitative: it's the first organizer I trust unattended,
precisely &lt;em&gt;because&lt;/em&gt; a bad filing costs one undo command and zero future
nights — the undo handles it, the memory remembers it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Prize Categories
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; (featured, $200) — &lt;code&gt;gemma3:4b&lt;/code&gt; is the primary classifier
in the consensus pair: schema-constrained by habit profile, corrected by user
memory, second-opinioned by a smaller model.&lt;/li&gt;
&lt;/ul&gt;

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