This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady.
What I Built
Sloth Sort takes the pile you keep not doing and hands almost all of it back.
You paste up to eight lines. A local Gemma model picks the one small thing that actually has a deadline, and it parks or skips the rest. The minutes you do not spend today are the point. On the sample pile, that was buying a Thursday train ticket, about ten minutes, and 230 minutes handed back on everything else: a dentist email, a Friday bill, Sunday lunch, a downloads folder, a blank-page resume, holiday browsing, and forty newsletters.
The second time a similar chore shows up, the app does not ask you to decide it from scratch. Tiger Data already has the earlier call.
Demo
The model stays on the machine, so this is a local run rather than a hosted URL.
Code
tharun634
/
sloth-sort
A local Gemma sorter that hands a dreaded pile back to you as one small move, and remembers the call in Tiger Data.
Sloth Sort
Dump the pile you keep putting off. A local Gemma model picks the one small thing worth doing today, and Tiger Data remembers the call so a similar chore does not get re-decided tomorrow.
This is a new project for the Hacktoberfest Open-Source AI Challenge: Week 3, Slow and Steady.
What it saves
A dreaded list is usually eight tasks pretending they all have to happen now. Sloth Sort hands back the minutes on everything except one small move. The first sample pile is there so you can feel that on a real model without inventing chores.
How it is built
flowchart LR
pile[Pasted pile] --> api[FastAPI]
api --> embed[EmbeddingGemma via Ollama]
embed --> tiger[(Tiger Data)]
tiger --> memory[BM25 plus pgvector]
memory --> gemma[Gemma 4 E4B via Ollama]
gemma --> api
api --> ledger[effort_events hypertable]
ledger --> chart[minutes_saved_daily]
-
Gemma 4 E4B (
gemma4:e4bin Ollama) is…
github.com/tharun634/sloth-sort. MIT. README.md has the run steps: Ollama, a Tiger Cloud connection string, then uvicorn.
How I Built It
flowchart LR
pile[Pasted pile] --> api[FastAPI]
api --> embed[EmbeddingGemma via Ollama]
embed --> tiger[(Tiger Data)]
tiger --> memory[BM25 plus pgvector]
memory --> gemma[Gemma 4 E4B via Ollama]
gemma --> api
api --> ledger[effort_events hypertable]
ledger --> chart[minutes_saved_daily]
Two open models, both through Ollama on localhost, and no SDK. A POST to /api/chat and /api/embed is the whole client. That is the same shape Damiano builds described for a one-shot local call: string in, string out, with a long timeout, because a local model is allowed to be slow.
Gemma 4 E4B (gemma4:e4b) is the sorter. It answers with one JSON object: exactly one do, and the rest split into later and skip. I turn thinking off. With it on, the model spent the token budget on a trace and the JSON came back cut in half.
EmbeddingGemma writes a halfvec(768) for every line. Those rows live in Tiger Data next to a BM25 index from pg_textsearch. For each new line the app runs both searches and fuses them with reciprocal rank fusion, k = 60, which is the hybrid pattern Tiger documents. The fused neighbors go into the prompt as hints, not orders. A dated bill or a ticket still beats a past social reply.
The decision table is a normal table. A vector column and Timescale columnstore do not belong on the same relation. The minutes are a separate append-only hypertable, effort_events, segmented by the decision, with a continuous aggregate minutes_saved_daily that the chart reads, including the rows from today.
pg_textsearch is still marked prerelease upstream. This is a DEV database, and the keyword half of the search is the reason to use it. The index did not answer a bare string. The query has to be item_text <@> to_bm25query(text, index_name).
The stack choice follows what people have already shipped. MindMirror is the same split I wanted for a pile of personal chores: FastAPI in front of Ollama, so the text is not a request to someone else's model.
Why Does Open Innovation Matter?
The pile is the sort of list you would not paste into a hosted chat: dentist, bills, mom, the mess in Downloads. Open weights are what make the laziness cheap enough to use. Gemma runs on this machine, EmbeddingGemma runs on this machine, and a sort does not send the lines to a model API. There is no per-token bill, so leaving the app running for the next pile costs nothing. The models are also swappable. The sorter and the embedder are environment variables. A closed API would have been faster to call and worse at the actual job, which is keeping a private list private.
My Agent Session
The session was recorded in Cursor with Grok. DEV's uploader has no Cursor tool name, so the session is filed under an accepted harness label. The transcript says that in the first reply. Paths, database URLs, and tokens are not in it.
The Lazy Payoff
I loaded the sample pile and sorted it on this machine.
- 15.9 seconds on Gemma 4 E4B, after thinking was turned off.
- One move: buy the Thursday train ticket, 10 minutes, because it was the nearest hard deadline.
- Held for later: the dentist email, the electricity bill due Friday, and Sunday lunch.
- Skipped: cleaning Downloads, rewriting a resume from a blank page, researching holidays, and clearing newsletters.
- 230 minutes handed back on that sort. The chart's bar for the day is that number, read from the continuous aggregate.
Prize Categories
- Best Use of Gemma. Gemma 4 E4B decides the pile. EmbeddingGemma is the memory. Both run locally through Ollama.
- Best Use of Tiger Data. Past decisions are retrieved with hybrid BM25 and pgvector search, and the minutes handed back are a hypertable plus a continuous aggregate.

Top comments (0)