<?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: Piotr Szutt</title>
    <description>The latest articles on DEV Community by Piotr Szutt (@schultzenator).</description>
    <link>https://dev.to/schultzenator</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%2F4200933%2F35b22858-57e9-4332-9180-40466c65f96d.png</url>
      <title>DEV Community: Piotr Szutt</title>
      <link>https://dev.to/schultzenator</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/schultzenator"/>
    <language>en</language>
    <item>
      <title>Tortoise: I Built a Productivity Tool That Plans *Less*, Not More (with an Open-Weight Model)</title>
      <dc:creator>Piotr Szutt</dc:creator>
      <pubDate>Thu, 22 Oct 2026 17:01:21 +0000</pubDate>
      <link>https://dev.to/schultzenator/tortoise-i-built-a-productivity-tool-that-plans-less-not-more-with-an-open-weight-model-1j6i</link>
      <guid>https://dev.to/schultzenator/tortoise-i-built-a-productivity-tool-that-plans-less-not-more-with-an-open-weight-model-1j6i</guid>
      <description>&lt;p&gt;Every productivity tool I have ever used has the same failure mode: it plans too much. Todo apps let you add infinite items. Calendars show you beautiful blocks of ambition that collapse the moment a meeting runs long. AI schedulers are worse - they will happily generate an eleven-hour "optimized" day and call it helpful.&lt;/p&gt;

&lt;p&gt;Then October hits, Hacktoberfest comes around, and the Week 3 theme of the DEV challenge - &lt;strong&gt;"Slow and Steady"&lt;/strong&gt; - asks for exactly the opposite: productivity through consistency, not intensity.&lt;/p&gt;

&lt;p&gt;So I built a tool with a deliberately anti-hustle bias: &lt;strong&gt;Tortoise&lt;/strong&gt;, a small CLI that turns your markdown backlog into a &lt;em&gt;tiny&lt;/em&gt; daily plan - exactly one anchor task plus at most two small support tasks, hard-capped by a focus budget you set. A Gemma model picks the anchor. Deterministic guardrails make sure the model can't cheat. A streak counter rewards the only thing that actually compounds: showing up again tomorrow.&lt;/p&gt;

&lt;p&gt;This post walks through what I built, why an open-weight model was the right call, and - the part I care about most - why the "boring" deterministic half of the design is the half that makes the whole thing trustworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: tools that optimize for the wrong thing
&lt;/h2&gt;

&lt;p&gt;Productivity software almost universally optimizes for &lt;em&gt;throughput&lt;/em&gt;: how many tasks can we help you capture, organize, schedule, and cram in? The result is a backlog that grows faster than you can shrink it, and a daily plan that is a wish rather than a commitment.&lt;/p&gt;

&lt;p&gt;The "slow and steady" insight is old - the tortoise beat the hare, consistency beats sprints, compound interest beats windfalls - but tooling rarely encodes it. What would a tool look like if it took the idea literally?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It would &lt;strong&gt;cap&lt;/strong&gt; your day, not expand it.&lt;/li&gt;
&lt;li&gt;It would prefer &lt;strong&gt;finishing&lt;/strong&gt; over starting.&lt;/li&gt;
&lt;li&gt;It would value &lt;strong&gt;yesterday's completed work&lt;/strong&gt; over today's ambitions.&lt;/li&gt;
&lt;li&gt;It would be small enough that you never argue with it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole spec for Tortoise.&lt;/p&gt;

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

&lt;p&gt;Tortoise is a zero-dependency Node CLI. Your backlog is a plain markdown file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Backlog&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] Draft the Q4 pricing page copy (50m)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Fix flaky checkout test (40m)
&lt;span class="p"&gt;-&lt;/span&gt; [x] Ship README quickstart
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Reply to the three partner emails (20m)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Rewrite onboarding email #2 (30m)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Sketch ideas for the conference talk (90m)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Update dependency lockfile (10m)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You give it today's focus budget and it produces a plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-strip-types&lt;/span&gt; src/cli.ts plan examples/backlog.md &lt;span class="nt"&gt;--budget&lt;/span&gt; 120
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Tortoise plan - 2026-10-22

Budget: 120 min | Source: mock

- [A] (ANCHOR, 10m) Update dependency lockfile
    why: smallest concrete step that moves work forward today (10m)
- [ ] (support, 20m) Reply to the three partner emails
    why: small enough to fit after the anchor (20m)
- [ ] (support, 30m) Rewrite onboarding email #2
    why: small enough to fit after the anchor (30m)

Total planned: 60m of 120m. Everything else waits. That is the point.

Deferred (not today): t1, t2, t6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that output again, because the interesting part is what it did &lt;strong&gt;not&lt;/strong&gt; do: it did not schedule the 90-minute conference talk. It did not fill the full 120-minute budget. It picked the 10-minute lockfile update as the anchor, two small tasks as support, and explicitly listed everything else as &lt;em&gt;deferred&lt;/em&gt;. Sixty planned minutes out of one hundred twenty available, by design.&lt;/p&gt;

&lt;p&gt;When you finish something, you check it off and the streak updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node &lt;span class="nt"&gt;--experimental-strip-types&lt;/span&gt; src/cli.ts &lt;span class="k"&gt;done &lt;/span&gt;examples/backlog.md t7
Done: Update dependency lockfile
Streak: 4 day&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the entire user experience. Three commands: &lt;code&gt;plan&lt;/code&gt;, &lt;code&gt;done&lt;/code&gt;, &lt;code&gt;streak&lt;/code&gt;. There is no sync, no dashboard, no notifications. Deliberately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why an open-weight model (Gemma) is the right tool here
&lt;/h2&gt;

&lt;p&gt;Task selection sounds like a trivial problem - until you look at what actually makes a good "anchor" for a day. It's a judgment call: which open item, out of everything you've written down, is &lt;em&gt;the&lt;/em&gt; thing that would make today count? Effort matters, but so does momentum ("prefer finishing over starting"), staleness, and how concrete the item is.&lt;/p&gt;

&lt;p&gt;That judgment call is exactly what small language models are decent at, and it's a great fit for open-weight Gemma:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's a constrained choice problem.&lt;/strong&gt; The model gets a numbered list with effort estimates and must return strict JSON: one anchor id plus up to two support ids. Small models handle this reliably. You don't need a frontier model to pick &lt;code&gt;t7&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open weights mean optionality.&lt;/strong&gt; The same prompt works against Google's hosted Gemma endpoint (free API key from AI Studio), or against a local Ollama instance with &lt;code&gt;gemma3:4b&lt;/code&gt; running on a laptop - no GPU cluster, no subscription, no vendor lock-in. For a personal tool that reads your task list, "runs fully offline" is a feature, not a footnote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The domain is small, so the model stays small.&lt;/strong&gt; This is the "slow and steady" philosophy applied to model choice too: don't reach for the biggest weights on the shelf when a 4B-parameter model does the job. Consistent, cheap, boring - on purpose.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The prompt is explicit about the philosophy, too. Tortoise tells the model: &lt;em&gt;smaller, concrete, older tasks beat big vague ones; prefer finishing over starting; do not invent tasks; do not rename tasks.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Model proposes, guardrails dispose
&lt;/h2&gt;

&lt;p&gt;Here is the part of the design I actually want to convince you of.&lt;/p&gt;

&lt;p&gt;An LLM in the critical path of your daily plan is a liability. It can hallucinate a task id. It can schedule a 90-minute anchor into a 20-minute gap. It can decide you deserve five tasks today. Any of those erodes the one thing a planning tool needs: that you trust the plan.&lt;/p&gt;

&lt;p&gt;So Tortoise treats the model as an &lt;em&gt;advisor with zero authority&lt;/em&gt;. Every selection it returns passes through deterministic guardrails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every selected id must exist in &lt;strong&gt;your&lt;/strong&gt; backlog and be open - the model cannot invent work;&lt;/li&gt;
&lt;li&gt;exactly one anchor, at most two supports - three items a day, hard cap;&lt;/li&gt;
&lt;li&gt;the anchor may not exceed 60% of the budget - the day can't be devoured by one task;&lt;/li&gt;
&lt;li&gt;the total may not exceed the budget - no wishful plans;&lt;/li&gt;
&lt;li&gt;effort estimates always come from the backlog, never from the model - it can't "estimate optimistically".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the output violates any rule - or the API is down, or the key expired, or the JSON is malformed - Tortoise doesn't error out or ask you to retry. It &lt;strong&gt;silently falls back&lt;/strong&gt; to a deterministic planner: smallest open task as the anchor, then small tasks that fit the remaining budget. It's a naive heuristic, and that's exactly why it's safe: it cannot fail in a creative way.&lt;/p&gt;

&lt;p&gt;The plan file even tells you which path produced it (&lt;code&gt;Source: mock&lt;/code&gt;, &lt;code&gt;Source: gemma-api&lt;/code&gt; or &lt;code&gt;Source: ollama&lt;/code&gt; when the model's selection was accepted, &lt;code&gt;Source: fallback&lt;/code&gt; when the deterministic planner took over), so you can tell when the model earned its keep.&lt;/p&gt;

&lt;p&gt;This "LLM proposes, code disposes" pattern is, I think, the underrated recipe for putting open-weight models into real tools. The model adds judgment; the code adds guarantees. You get the interesting half of AI without the fragility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lazy payoff: why the fallback is a feature
&lt;/h2&gt;

&lt;p&gt;There's a lazy-person's argument for this architecture, and it's a strong one. The fallback planner is maybe twenty lines of sorting. The guardrail validator is maybe thirty. Together they mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I never have to debug "the model did something weird" at 9am before coffee - I just get the deterministic plan.&lt;/li&gt;
&lt;li&gt;The test suite (43 assertions, zero dependencies) can cover every failure mode with mock providers, including a provider that throws, one that returns hallucinated ids, and one that returns garbage JSON.&lt;/li&gt;
&lt;li&gt;The tool degrades the way infrastructure should degrade: smaller, quieter, still working.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The "slow and steady" payoff isn't just the streak counter. It's that a tool you can trust on a bad day is a tool you still use on day forty. Fancy output you have to babysit is a tool you abandon in week two. Consistency compounds - for backlogs and for software design alike.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streaks: measuring the only thing that matters
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;streak&lt;/code&gt; command reads a one-line-per-completion log (&lt;code&gt;log.jsonl&lt;/code&gt;) and counts consecutive days with at least one completed task. If today isn't logged yet, it counts from yesterday - the streak is never "broken" before the day is over.&lt;/p&gt;

&lt;p&gt;That's the entire gamification. No points, no levels, no streak-freeze monetization. Just a number that goes up only if you keep showing up, and which resets to zero after a gap. It's the honest version of the mechanic: the number has no value except the value you give it, and you give it value precisely by not wanting to see it reset.&lt;/p&gt;

&lt;p&gt;A ten-day streak of one finished anchor per day is one hundred ten minutes of real, compounding progress (10m + 20m + 30m... well, roughly - the math is yours to make honest). A ten-day streak of over-planned days is usually zero finished work and a growing resentment of your to-do app. The streak counter makes the difference visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run it
&lt;/h2&gt;

&lt;p&gt;Everything is in the repo, under &lt;code&gt;dev-hf26-week3/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Schultzenator/HCTN.git
&lt;span class="nb"&gt;cd &lt;/span&gt;HCTN/dev-hf26-week3

&lt;span class="c"&gt;# offline demo, no API key (deterministic mock provider)&lt;/span&gt;
npm run demo

&lt;span class="c"&gt;# with Gemma via the Google Generative Language API&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GEMMA_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-google-ai-studio-key
node &lt;span class="nt"&gt;--experimental-strip-types&lt;/span&gt; src/cli.ts plan examples/backlog.md &lt;span class="nt"&gt;--provider&lt;/span&gt; gemma &lt;span class="nt"&gt;--budget&lt;/span&gt; 120

&lt;span class="c"&gt;# fully local open weights (ollama pull gemma3:4b)&lt;/span&gt;
node &lt;span class="nt"&gt;--experimental-strip-types&lt;/span&gt; src/cli.ts plan examples/backlog.md &lt;span class="nt"&gt;--provider&lt;/span&gt; ollama &lt;span class="nt"&gt;--budget&lt;/span&gt; 90

&lt;span class="c"&gt;# tests: 43 assertions, zero dependencies&lt;/span&gt;
npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No install step beyond cloning - Tortoise has &lt;strong&gt;zero npm dependencies&lt;/strong&gt;, using only Node's standard library (22.6+, for native TypeScript stripping). For a tool meant to be the last thing you run before starting work, "npm install resolves nothing" is its own kind of quiet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I deliberately left out
&lt;/h2&gt;

&lt;p&gt;Scope discipline is the "slow and steady" theme turned inward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No calendar integration.&lt;/strong&gt; The budget is a number you type, not a negotiation with your calendar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No notifications.&lt;/strong&gt; The tool runs when you run it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No server, no account, no cloud.&lt;/strong&gt; Your tasks are a markdown file; your log is a JSONL file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No multi-agent anything.&lt;/strong&gt; One small model, one clear job.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of those would have been a weekend of fun code and a permanent liability. The narrowness is what made it possible to build the guardrails properly and to test every path the model can't be trusted with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Submission details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project name:&lt;/strong&gt; Tortoise - a slow-and-steady daily planner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; turns a markdown backlog into a minimal daily plan (one anchor + up to two support tasks, hard budget cap), with a streak counter for consecutive finished days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open model used:&lt;/strong&gt; Gemma (open weights) via the Google Generative Language API (&lt;code&gt;gemma-3-12b-it&lt;/code&gt;), with a fully-local Ollama option (&lt;code&gt;gemma3:4b&lt;/code&gt;) and a deterministic offline provider for tests/demos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tech:&lt;/strong&gt; TypeScript, Node &amp;gt;= 22.6, zero npm dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prize categories entered:&lt;/strong&gt; Best Use of Gemma&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/Schultzenator/HCTN/tree/main/dev-hf26-week3" rel="noopener noreferrer"&gt;https://github.com/Schultzenator/HCTN/tree/main/dev-hf26-week3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo:&lt;/strong&gt; clone, run &lt;code&gt;npm run demo&lt;/code&gt; (offline, deterministic); or run with your own &lt;code&gt;GEMMA_API_KEY&lt;/code&gt; / local Ollama for live model selection&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;"Slow and steady" usually gets presented as a virtue - patience, persistence, modest expectations. Tortoise is my attempt to encode it as &lt;em&gt;mechanics&lt;/em&gt;: a planner that structurally cannot over-plan, that defers greed to the deterministic layer, and that counts days instead of hours.&lt;/p&gt;

&lt;p&gt;The interesting design lesson for me was the division of labor: give the open-weight model the judgment call (what matters most today), and give boring, typed, well-tested code all the authority (budgets, caps, existence checks, fallbacks). The model makes the plan interesting; the guardrails make the plan &lt;em&gt;true&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you want to try it, the quickstart above is genuinely all there is - clone, run, done. And if you're building with open models this Hacktoberfest, I'd encourage stealing the pattern wholesale: small model, narrow job, deterministic veto. It's the slow, steady way to ship AI tools that don't bite.&lt;/p&gt;

&lt;p&gt;The tortoise had it right all along. It just needed better tooling.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Happy Hacktoberfest - see you in the backlog.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hf26challenge</category>
      <category>devchallenge</category>
      <category>gemma</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
