<?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: Harsh Tuli</title>
    <description>The latest articles on DEV Community by Harsh Tuli (@452harsh).</description>
    <link>https://dev.to/452harsh</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%2F4159262%2F05b9386b-5ffe-4b2e-a171-184f0463c540.png</url>
      <title>DEV Community: Harsh Tuli</title>
      <link>https://dev.to/452harsh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/452harsh"/>
    <language>en</language>
    <item>
      <title>SlothScroll: I Taught Gemma to Read Instagram So I Never Have To</title>
      <dc:creator>Harsh Tuli</dc:creator>
      <pubDate>Sat, 24 Oct 2026 08:01:27 +0000</pubDate>
      <link>https://dev.to/452harsh/slothscroll-i-taught-gemma-to-read-instagram-so-i-never-have-to-5f9f</link>
      <guid>https://dev.to/452harsh/slothscroll-i-taught-gemma-to-read-instagram-so-i-never-have-to-5f9f</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;Every morning I opened Instagram "just to catch up", and every morning the feed won. One quick check became another, with ads, reposts and giveaways in between.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SlothScroll&lt;/strong&gt; is my lazy fix. It's a morning digest of the Instagram pages I follow. Once a day a cloud browser visits those pages for me. &lt;strong&gt;Gemma&lt;/strong&gt;, Google's open-weight model, reads every new post and writes a one-line gist. It also sorts the post into a topic (Sports, Science, Finance, Tech…) and quietly drops ads, reposts and giveaways. What I get is &lt;strong&gt;one calm page and one email&lt;/strong&gt;, grouped by topic, with the 1–3 posts actually worth opening.&lt;/p&gt;

&lt;p&gt;I read it in about two minutes with my coffee, then close the tab. I never open Instagram.&lt;/p&gt;

&lt;p&gt;Who gets to be lazier? Anyone who follows news, sports or creator pages to &lt;em&gt;stay informed&lt;/em&gt;, not to scroll.&lt;/p&gt;

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

&lt;p&gt;🔗 &lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://slothscroll-web-ngb7.onrender.com" rel="noopener noreferrer"&gt;https://slothscroll-web-ngb7.onrender.com&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;What you'll see in the video:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Today's edition:&lt;/strong&gt; a greeting, the "scrolling saved" number, topic tabs, and Gemma's summary for each topic with its top picks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The archive&lt;/strong&gt;, an RSS feed per page, and dark mode&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The morning email&lt;/strong&gt;, the same edition delivered to my inbox&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&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/452Harsh" rel="noopener noreferrer"&gt;
        452Harsh
      &lt;/a&gt; / &lt;a href="https://github.com/452Harsh/SnoozeFeed" rel="noopener noreferrer"&gt;
        SnoozeFeed
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The latest code is on the &lt;a href="https://github.com/452Harsh/SnoozeFeed/tree/SlothScroll" rel="noopener noreferrer"&gt;&lt;code&gt;SlothScroll&lt;/code&gt; branch&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&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%2Fyj268if7fw3sdaad8756.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%2Fyj268if7fw3sdaad8756.png" alt="How SlothScroll works: Render Cron triggers Browserbase and Stagehand, Gemma writes gists, topics and summaries, MongoDB Atlas stores everything, and the edition is served on Render and emailed through Resend" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma (&lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt;)&lt;/strong&gt; is the brain. It runs through an OpenAI-compatible client set by environment variables (&lt;code&gt;AI_BASE_URL&lt;/code&gt;, &lt;code&gt;AI_MODEL&lt;/code&gt;), so the same code can point at a hosted endpoint or, by changing one variable, a local Ollama. I use it for three jobs:

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Per post:&lt;/strong&gt; a one-line gist, the post's topic, and whether it's an ad, repost or giveaway. All three come back as JSON from a single call. When a caption is only emojis and hashtags, Gemma looks at the image instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per page:&lt;/strong&gt; a fallback category when I don't set one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per topic:&lt;/strong&gt; a 2–3 line summary plus the posts worth opening.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browserbase + Stagehand&lt;/strong&gt; give it a real browser. I log in once through Browserbase's live view, and a persisted context keeps that session for daily runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB Atlas&lt;/strong&gt; is the data layer. The cron job writes, the website reads, and a unique index on the post URL means a post is never processed twice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render&lt;/strong&gt; runs everything from one &lt;code&gt;render.yaml&lt;/code&gt; Blueprint: a &lt;strong&gt;cron job&lt;/strong&gt; for the daily pipeline and a &lt;strong&gt;web service&lt;/strong&gt; for the site. Every secret is &lt;code&gt;sync: false&lt;/code&gt;, so none live in the repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The website&lt;/strong&gt; is server-rendered HTML on Hono, with no frontend framework. It has light and dark mode, works on mobile, and loads quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElevenLabs&lt;/strong&gt; narrated the demo video.&lt;/li&gt;
&lt;/ul&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%2F2wvpzuo0wiglm35pu7xp.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%2F2wvpzuo0wiglm35pu7xp.png" alt="Today's edition on the live site: a greeting, minutes of scrolling saved, topic tabs, and Gemma's summary with the posts worth opening" width="800" height="556"&gt;&lt;/a&gt;&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%2Ft8zbo7jluql564233yi4.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%2Ft8zbo7jluql564233yi4.png" alt="The same edition in the morning email" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Slow and steady by design:&lt;/strong&gt; one run per day, public pages only, visited one at a time with random pauses. A page that fails is noted, and the edition still ships. If Instagram asks for a login, SlothScroll &lt;strong&gt;stops and tells me&lt;/strong&gt; instead of trying to get around it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What broke (and how I fixed it)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma was slow for this job.&lt;/strong&gt; A demo run took about 4 minutes because the model reasoned before every answer. Setting &lt;code&gt;reasoning_effort: "minimal"&lt;/code&gt; brought it down to &lt;strong&gt;about 20 seconds&lt;/strong&gt;, with the same quality for one-line gists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything was "News".&lt;/strong&gt; Topics were assigned per page, so a football page's posts and an IPO story all landed in News. Now Gemma chooses a topic &lt;strong&gt;per post&lt;/strong&gt; in the same call, with no extra requests. On my real feed the same 16 posts went from one topic to five.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The browser died mid-run.&lt;/strong&gt; Browserbase sessions end after 5 minutes by default, and a full run takes longer. Asking for a 30-minute session fixed it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Half-emoji headlines.&lt;/strong&gt; Shortening captions sometimes cut an emoji in half and left "�". The page now cleans those characters before showing them.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;What you follow says a lot about you. Your reading habits are personal, and an open-weight model means &lt;strong&gt;this digest doesn't have to depend on one company's closed API&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;I choose where it runs.&lt;/strong&gt; Gemma is reached through a plain OpenAI-compatible endpoint, so switching providers is an environment-variable change. The client is built to switch to a local Ollama the same way, so the posts never have to leave my machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's cheap enough to run every day forever.&lt;/strong&gt; A small open model is plenty for one-line gists and topic tags. It's a daily habit, not a premium feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's inspectable and forkable.&lt;/strong&gt; The prompts, the pipeline and the model are all open, so anyone can adapt SlothScroll to their own pages, language or topics.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;From a real run this week, using a deliberately conservative estimate of &lt;strong&gt;8 seconds per post&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;16 posts&lt;/strong&gt; from my pages became &lt;strong&gt;5 topics&lt;/strong&gt; and one page, about &lt;strong&gt;2 minutes of scrolling saved&lt;/strong&gt; that day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0 times&lt;/strong&gt; I opened Instagram.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The real win is bigger than the 8-second math. One "quick check" usually turns into twenty minutes. SlothScroll ends the session before it starts, and it scales with the pages you follow: I've since added six more (The Verge, National Geographic, CNBC, Tasty, Netflix and 9GAG).&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; Gemma writes the gists, picks the topics, filters ads and summarizes each topic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render:&lt;/strong&gt; a Blueprint with a cron job for the agent pipeline and a web service for the front end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Browserbase:&lt;/strong&gt; Stagehand drives a real browser with a persisted login, as a repeatable daily workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of MongoDB Atlas:&lt;/strong&gt; the database behind an app built on an open-weight model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs:&lt;/strong&gt; narration for the demo video.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>Outward: an adventure story that only moves forward when you go outside</title>
      <dc:creator>Harsh Tuli</dc:creator>
      <pubDate>Sun, 11 Oct 2026 05:43:36 +0000</pubDate>
      <link>https://dev.to/452harsh/outward-an-adventure-story-that-only-moves-forward-when-you-go-outside-a6h</link>
      <guid>https://dev.to/452harsh/outward-an-adventure-story-that-only-moves-forward-when-you-go-outside-a6h</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;Outward&lt;/strong&gt; is a story game you can't play from the sofa. You are the main character of an adventure, and the next chapter only unlocks when you go outside, finish a small real-world mission, and tell the story what you found.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"The Archivist needs something the city has forgotten. Walk for ten minutes and find a mark someone left on purpose."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You pick 10, 30 or 60 minutes, put the phone in your pocket, and walk. When you've actually covered the distance (250 m, 700 m or 1.4 km, tracked by GPS), Outward wakes up and asks what you found: &lt;em&gt;"a chalk heart drawn on the pavement."&lt;/em&gt; A small language model &lt;strong&gt;running on your phone&lt;/strong&gt; writes that chalk heart into the next part of the story, ends on a cliffhanger, and the chapter is done. Your find goes into a field journal, and Sprig, a small leaf-sprout companion, celebrates with you.&lt;/p&gt;

&lt;p&gt;It's for anyone whose screen time has quietly crept past their outside time, including me. Most "go outside" apps use guilt (streaks, step counts, red rings). Outward uses curiosity instead: you go outside because you want to know what happens next, and the story can't happen without something you saw with your own eyes.&lt;/p&gt;

&lt;p&gt;Three things I cared about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The story is really about your walk.&lt;/strong&gt; Every chapter is written around &lt;em&gt;your&lt;/em&gt; discovery, not picked from a list. The puddle, the cat on the wall and the chalk heart become plot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phone in pocket, not in hand.&lt;/strong&gt; During a mission the screen tells you to put it away. There's no map to stare at and no feed. The app counts &lt;em&gt;screen-off&lt;/em&gt; time as a good thing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It works with no signal.&lt;/strong&gt; After one install, the whole thing runs in airplane mode: the storyteller model, voice-to-text, narration and safety rules. Parks, trails and basements don't break it.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Two partners, two jobs:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🧠 &lt;strong&gt;Tinker trains the brain.&lt;/strong&gt; I fine-tuned Qwen3.5-4B on Tinker and distilled it into a 241 MB Gemma that writes almost as well as its 4B teacher (4.75 vs 4.79). Total cost: &lt;strong&gt;$2.56&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;🚚 &lt;strong&gt;Render delivers it.&lt;/strong&gt; One Render service ships the app, the model weights and new story seasons to every phone, with the browser headers that make on-device AI fast and a guardrail on bandwidth cost. After that, the phone needs no internet at all.&lt;/p&gt;
&lt;/blockquote&gt;

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

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

&lt;p&gt;🔗 &lt;strong&gt;Live on Render:&lt;/strong&gt; &lt;a href="https://outward-6fhh.onrender.com" rel="noopener noreferrer"&gt;https://outward-6fhh.onrender.com&lt;/a&gt; (install it as an app from the browser menu)&lt;/p&gt;

&lt;p&gt;🩺 &lt;strong&gt;Live server status:&lt;/strong&gt; &lt;a href="https://outward-6fhh.onrender.com/api/health" rel="noopener noreferrer"&gt;https://outward-6fhh.onrender.com/api/health&lt;/a&gt; (deployed commit, model ready on the Render disk, bandwidth used this month)&lt;/p&gt;

&lt;p&gt;What to try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open it once on Wi-Fi.&lt;/strong&gt; The setup screen "packs your bag": it downloads the 241 MB storyteller and the 44 MB voice model, then tells you it's safe to go offline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn on airplane mode&lt;/strong&gt; and start Chapter 1. Everything still works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No time to walk right now?&lt;/strong&gt; &lt;em&gt;Settings → Practice mode&lt;/em&gt; skips the walking check so you can see the whole loop at your desk. (Real chapters do want the walk.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settings → Diagnostics&lt;/strong&gt; shows which model is installed, tokens per second on your phone, and the last chapters it wrote.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the video, the chapter after "a chalk heart drawn on the pavement" is written live with the network switched off, in about 2–3 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&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/452Harsh" rel="noopener noreferrer"&gt;
        452Harsh
      &lt;/a&gt; / &lt;a href="https://github.com/452Harsh/outward" rel="noopener noreferrer"&gt;
        outward
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Outward: your story happens outside. Offline PWA with an on-device Gemma storyteller.
    &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;Outward&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Your story happens outside.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Outward is an installable web app (PWA) that turns a walk into an adventure. You are the main character. The story moves forward only when you go outside and complete real-world missions, like &lt;em&gt;"find something that flows."&lt;/em&gt; You describe what you found, and a small open-weight model running &lt;strong&gt;on your phone&lt;/strong&gt; weaves it into the story.&lt;/p&gt;
&lt;p&gt;After one install, it works fully offline: no signal needed, and nothing about your location, voice or walks leaves the phone.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Phase 7 of 8. The offline PWA, the 5-chapter story, an on-device Gemma 3 270M storyteller and on-device voice input (Whisper) work in airplane mode, along with walking-gated missions, a drawn walk path and screen-time stats. The phone now runs a Gemma 3 270M that was distilled from a Tinker fine-tuned Qwen3.5-4B (rubric 2.55 → 4.75; see &lt;a href="https://github.com/452Harsh/outward/training/RESULTS.md" rel="noopener noreferrer"&gt;training/RESULTS.md&lt;/a&gt;). A story factory ships new reviewed, narrated story…&lt;/p&gt;
&lt;/blockquote&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/452Harsh/outward" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;A monorepo: &lt;code&gt;/app&lt;/code&gt; (the PWA), &lt;code&gt;/server&lt;/code&gt; (Render), &lt;code&gt;/training&lt;/code&gt; (Tinker fine-tune, distillation and evals), &lt;code&gt;/content&lt;/code&gt; (stories, safety rules, prompts and the model registry). It's MIT licensed, with the Gemma weights under Gemma Terms.&lt;/p&gt;

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

&lt;p&gt;The core problem: &lt;strong&gt;a good storyteller is a 4B model, but a phone in a park needs something about 15× smaller that never calls home.&lt;/strong&gt; So I used open-weight models end to end: I trained a teacher in the cloud, distilled it into a tiny student, and shipped the student to the phone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                       ┌──────────── TRAINING (one-off, ~$2.56) ────────────┐
 648 hand-checked  ──► │ Qwen3.5-4B ──LoRA on Tinker──► Teacher v3          │
 seed chapters         │                                   │                │
                       │                     2,500 prompts × 3 samples      │
                       │                     rubric + safety filter         │
                       │                                   ▼                │
                       │        3,186 examples ──► Gemma 3 270M (Kaggle)    │
                       │                           full fine-tune ► GGUF Q4 │
                       └───────────────────────────────────┬────────────────┘
                                                           ▼
  Render (Starter, Singapore)                 Phone (offline after install)
  ┌──────────────────────────┐   one-time    ┌───────────────────────────────┐
  │ Fastify: PWA + /models   │──download────►│ Preact PWA + Service Worker   │
  │ 1 GB disk, HF fallback   │               │ wllama (llama.cpp → WASM)     │
  │ /api/packs (new seasons) │               │ Whisper tiny.en (ONNX, WASM)  │
  └──────────────────────────┘               │ Safety filter (JS)            │
                                             │ GPS walk gate · IndexedDB     │
                                             └───────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Open-source AI
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Model / tool&lt;/th&gt;
&lt;th&gt;Where it runs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Teacher storyteller&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Qwen3.5-4B&lt;/strong&gt; + LoRA r32&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Tinker&lt;/strong&gt; (training + sampling)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On-device storyteller&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Gemma 3 270M&lt;/strong&gt;, distilled, Q4_0 GGUF (241 MB)&lt;/td&gt;
&lt;td&gt;The phone, via &lt;strong&gt;wllama&lt;/strong&gt; (llama.cpp WASM, WebGPU or CPU)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optional bigger model&lt;/td&gt;
&lt;td&gt;Gemma 3 1B QAT Q4_0 (720 MB)&lt;/td&gt;
&lt;td&gt;The phone, downloaded from Hugging Face&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Voice → text&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Whisper tiny.en&lt;/strong&gt; int8 (44 MB)&lt;/td&gt;
&lt;td&gt;The phone, via &lt;strong&gt;Transformers.js&lt;/strong&gt; + ONNX Runtime WASM in a worker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Student training&lt;/td&gt;
&lt;td&gt;Hugging Face &lt;strong&gt;TRL&lt;/strong&gt; SFT&lt;/td&gt;
&lt;td&gt;Kaggle notebook (free GPU)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversion&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;llama.cpp&lt;/strong&gt; &lt;code&gt;convert_hf_to_gguf&lt;/code&gt; + &lt;code&gt;llama-quantize&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;My Mac&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App, model and story delivery&lt;/td&gt;
&lt;td&gt;Fastify + persistent disk&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Render&lt;/strong&gt; (Starter, Singapore)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 1: Fine-tune the teacher on Tinker
&lt;/h3&gt;

&lt;p&gt;Base Qwen3.5-4B already writes decent prose. The problem is that it &lt;strong&gt;ignores the player&lt;/strong&gt;: it mentions your discovery and then carries on with its own plot. In a story whose whole point is "you shaped this," that's the main failure.&lt;/p&gt;

&lt;p&gt;I wrote 648 seed chapters, built a 5-point rubric (uses the discovery, coherent, steers the plot, safe, length), and trained a LoRA with per-epoch checkpoints. I picked the checkpoint by validation loss after my first run overfit. Results on 50 held-out prompts × 3 samples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Base Qwen3.5-4B&lt;/th&gt;
&lt;th&gt;Fine-tuned (v3)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rubric score (/5)&lt;/td&gt;
&lt;td&gt;4.66&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.79&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coherent&lt;/td&gt;
&lt;td&gt;93%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;99%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discovery actually steers the plot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;39%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Length&lt;/td&gt;
&lt;td&gt;93 words&lt;/td&gt;
&lt;td&gt;82 words (closer to target)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Steering went from 6% to 39%, about &lt;strong&gt;6.5×&lt;/strong&gt; better on the property the game depends on. It also held up on a separate dev set (12% → 30%). To be honest about it: the safety checker flagged slightly more outputs (4 → 6), and every one of those came from the player's own words being quoted back, not from anything the model made up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Distil it into something a phone can run
&lt;/h3&gt;

&lt;p&gt;A 4B model doesn't belong in a browser tab on a phone. The teacher generated 2,500 prompts × 3 samples. Each sample was scored by the rubric and the safety filter, leaving &lt;strong&gt;3,186&lt;/strong&gt; good examples. I then full-fine-tuned &lt;strong&gt;Gemma 3 270M&lt;/strong&gt; on them in a Kaggle notebook and quantised it to Q4_0 GGUF.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Same 50 eval prompts&lt;/th&gt;
&lt;th&gt;Score (/5)&lt;/th&gt;
&lt;th&gt;Unsafe flags&lt;/th&gt;
&lt;th&gt;Steers plot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stock Gemma 3 270M&lt;/td&gt;
&lt;td&gt;2.55&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stock 270M + my best one-shot prompt&lt;/td&gt;
&lt;td&gt;3.66&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;17%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Distilled Outward student (270M)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.75&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;44%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Teacher (4B, cloud)&lt;/td&gt;
&lt;td&gt;4.79&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;39%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A 270M model at 241 MB comes within 0.04 points of its 4B teacher.&lt;/strong&gt; It writes a chapter in &lt;strong&gt;2–3 seconds&lt;/strong&gt; on-device (≈53 tokens/s on CPU) at zero cost per chapter. It also wrote a good chapter for a story season it never saw in training.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Total Tinker spend for the whole project: $2.56.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Keep it safe outdoors
&lt;/h3&gt;

&lt;p&gt;A game that sends people outside has to be careful about &lt;em&gt;where&lt;/em&gt;. Missions never ask for roads, water edges, strangers, private property, climbing or the dark. Safety rules live in one &lt;code&gt;rules.json&lt;/code&gt; that both a &lt;strong&gt;JavaScript filter (on the phone)&lt;/strong&gt; and a &lt;strong&gt;Python filter (in training)&lt;/strong&gt; use. A shared test file of cases keeps them in sync, and CI fails if they disagree. The filter also checks everything the model writes: it must use the discovery, stay on length, not copy the example or echo the prompt, and not talk about being an AI. If an output fails, the player gets a hand-written fallback instead. The app shows which one you got ("Written on this phone" vs "Pre-written").&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Render, the runtime that gets AI onto the phone
&lt;/h3&gt;

&lt;p&gt;An on-device model still has to &lt;em&gt;reach&lt;/em&gt; the device: 241 MB of weights, a WASM runtime that needs special browser headers, and new stories after launch. &lt;strong&gt;Render is that delivery layer.&lt;/strong&gt; It's one Node service (Fastify) on the Starter plan in Singapore, the closest region to me in India, deployed from a single &lt;a href="https://github.com/452Harsh/outward/blob/main/render.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;render.yaml&lt;/code&gt;&lt;/a&gt; Blueprint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; git push ──► Render build (npm ci &amp;amp;&amp;amp; npm run build) ──► health check /api/health
                                                              │
 content/models.json ──(pinned HF revision)──► boot sync ──► 1 GB disk /var/data/models
                                                              │
 phone ◄── PWA + COOP/COEP ◄── /models/&amp;lt;id&amp;gt;/&amp;lt;version&amp;gt;/…gguf ◄─┤  (over budget → 302 to Hugging Face)
       ◄── /api/packs + /packs/&amp;lt;id&amp;gt;/audio/*.mp3 (new seasons)◄┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What Render does, and why each part matters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It makes fast on-device inference possible.&lt;/strong&gt; Every response carries &lt;code&gt;Cross-Origin-Opener-Policy&lt;/code&gt; and &lt;code&gt;Cross-Origin-Embedder-Policy&lt;/code&gt;. That &lt;em&gt;cross-origin isolation&lt;/em&gt; unlocks &lt;code&gt;SharedArrayBuffer&lt;/code&gt;, which llama.cpp's WASM build needs to run on several CPU threads. Without these two headers, the phone's storyteller falls back to a single thread. A plain static host where you can't set headers wouldn't have worked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The persistent disk is the model store.&lt;/strong&gt; On boot, the server reads &lt;code&gt;content/models.json&lt;/code&gt;, downloads the exact pinned Hugging Face revision of the student model to the 1 GB disk (as a &lt;code&gt;.part&lt;/code&gt; file, size-checked, then renamed), and deletes old versions so the disk never fills. &lt;strong&gt;Shipping a better model is just a registry entry and a &lt;code&gt;git push&lt;/code&gt;:&lt;/strong&gt; Render rebuilds, the disk syncs, and phones see a new versioned URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching is designed for an offline app.&lt;/strong&gt; Model files and hashed assets are served &lt;code&gt;immutable&lt;/code&gt; for a year, so a phone never downloads 241 MB twice. &lt;code&gt;index.html&lt;/code&gt; and the service worker are &lt;code&gt;no-cache&lt;/code&gt;, so app updates still reach installed phones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Costs have a guardrail.&lt;/strong&gt; The server counts model bytes it sends each month. After &lt;code&gt;MODEL_BANDWIDTH_BUDGET_GB&lt;/code&gt; (60 GB), &lt;code&gt;/models/*&lt;/code&gt; answers with a 302 redirect to the identical file on Hugging Face. The app also tries Render first and falls back to Hugging Face by itself. Hosting stays at about &lt;strong&gt;$7.25/month&lt;/strong&gt;, and 100 installs come to roughly &lt;strong&gt;$11&lt;/strong&gt;. The worst case is capped instead of open-ended.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New story seasons arrive without an app update.&lt;/strong&gt; &lt;code&gt;/api/packs&lt;/code&gt; lists approved story packs, and their ElevenLabs narration is served from content-hashed URLs. The app picks them up and caches them for offline play. That's how Season 2 (&lt;em&gt;The Second Atlas&lt;/em&gt;) shipped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No secrets on the server.&lt;/strong&gt; Render never runs a model and holds no API keys. Tinker and ElevenLabs are only used in the offline story factory on my Mac. If someone breaks into the server, there's nothing to steal and no bill to run up.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's live, and you can check it yourself:&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;curl https://outward-6fhh.onrender.com/api/health
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;:true,&lt;span class="s2"&gt;"commit"&lt;/span&gt;:&lt;span class="s2"&gt;"475692a"&lt;/span&gt;,&lt;span class="s2"&gt;"app"&lt;/span&gt;:true,
 &lt;span class="s2"&gt;"models"&lt;/span&gt;:[&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"outward-gemma3-270m"&lt;/span&gt;,&lt;span class="s2"&gt;"version"&lt;/span&gt;:&lt;span class="s2"&gt;"distilled-v1-133f250"&lt;/span&gt;,&lt;span class="s2"&gt;"ready"&lt;/span&gt;:true&lt;span class="o"&gt;}]&lt;/span&gt;,
 &lt;span class="s2"&gt;"bandwidth"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"month"&lt;/span&gt;:&lt;span class="s2"&gt;"2026-10"&lt;/span&gt;,&lt;span class="s2"&gt;"gb"&lt;/span&gt;:0,&lt;span class="s2"&gt;"budgetGB"&lt;/span&gt;:60&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"packs"&lt;/span&gt;:[&lt;span class="s2"&gt;"second-atlas"&lt;/span&gt;&lt;span class="o"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I deliberately &lt;strong&gt;didn't&lt;/strong&gt; run the model on Render. A cloud model would need a connection on every walk and would see every user's location. Here, Render does the job a server is good at: shipping, versioning, updating and guarding the cost. The phone does the thinking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Voice, story seasons and CI
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ElevenLabs&lt;/strong&gt; narrates the fixed story text (George, as &lt;em&gt;the Archivist&lt;/em&gt;). The clips are rendered once and cached for offline use, about 2.4 MB in total. The model's live text is read by the phone's own voice, with karaoke-style captions and Sprig's mouth synced to the audio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New seasons:&lt;/strong&gt; a "story factory" on my Mac takes a human-written plot outline, drafts prose with Qwen on Tinker, applies my edits, renders narration, and only publishes a pack after I approve it. Season 2 (&lt;em&gt;The Second Atlas&lt;/em&gt;) cost &lt;strong&gt;$0.007&lt;/strong&gt; in Tinker credits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions&lt;/strong&gt; runs lint, the safety-parity tests, app and server tests, the build and the Python tests on every push.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install size:&lt;/strong&gt; 311 MB total (model 241, Whisper 44, runtimes 24, narration 2.4, app 0.1).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Because the whole idea doesn't work with a closed API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Go outside" and "needs a connection" don't go together.&lt;/strong&gt; The moments Outward is built for (a trail, a park, a basement café, a country with expensive roaming) are exactly where cloud APIs fail. Open weights mean the storyteller travels &lt;em&gt;with&lt;/em&gt; you. After one download it belongs to you: no rate limit, no outage, no key in the client, and no bill that grows with every walk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy isn't a setting here; it comes from where the model runs.&lt;/strong&gt; Outward knows where you walk and what you noticed. With a hosted model, every chapter would send your location and your words to someone else's server. Here, the walk, the voice recording and the story never leave the phone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open weights let me change the model, not just the prompt.&lt;/strong&gt; The biggest jump in quality didn't come from prompt engineering (stock 270M with my best prompt reached 3.66). It came from fine-tuning a teacher on Tinker and distilling it into a 270M student (4.75). You can only do that when you can touch the weights. With a closed API I'd have been stuck asking a model 15× bigger, over the network, to please pay more attention to the chalk heart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open weights are weights I'm allowed to host.&lt;/strong&gt; Because Gemma and my distilled student can be redistributed, the model sits on &lt;em&gt;my&lt;/em&gt; Render disk, versioned next to the app that uses it and cached forever on the phone. A closed model can't be downloaded at all, let alone hosted on a $7 server and carried into a park.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And it was cheap enough for one person in a week.&lt;/strong&gt; Qwen and Gemma weights, llama.cpp, wllama, Whisper, Transformers.js, ONNX Runtime and TRL are all open. The cloud cost of training both models was &lt;strong&gt;$2.56&lt;/strong&gt;. Everything I trained is published too: the student weights are on Hugging Face, and the training scripts, evals, rubric and safety rules are in the repo, so someone else can build &lt;em&gt;their&lt;/em&gt; outdoor story on top of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;I built Outward with &lt;strong&gt;Claude Code&lt;/strong&gt; as my pair programmer. It helped with everything from the Tinker training loop and the eval rubric to the wllama integration, the safety-filter parity tests and the demo-video tooling. I made the product, cost and account decisions; it helped with the implementation and the debugging. One example: it found that the stock 270M "passed" my first rubric by copying the example story word for word, which is why the &lt;code&gt;copies_example&lt;/code&gt; and &lt;code&gt;echoes_prompt&lt;/code&gt; checks exist.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Tinker:&lt;/strong&gt; Qwen3.5-4B LoRA fine-tune on Tinker. The plot-steering rate went from 6% to 39% over base, the teacher was distilled into a 270M on-device student that scores 4.75 vs the teacher's 4.79, and the whole project cost $2.56.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render:&lt;/strong&gt; Render is the runtime that delivers on-device AI, deployed from one &lt;code&gt;render.yaml&lt;/code&gt; Blueprint. It hosts the AI app's front end with the cross-origin isolation headers that multithreaded llama.cpp WASM needs. It keeps the versioned model weights on a persistent disk that syncs from a pinned Hugging Face revision on boot. A monthly bandwidth budget redirects to Hugging Face once it's used up, and new story seasons ship through &lt;code&gt;/api/packs&lt;/code&gt;. A live &lt;code&gt;/api/health&lt;/code&gt; endpoint shows the deployed commit, model readiness and bandwidth use.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>Patternwise: Stop Grinding. Start Learning Patterns. Your Open-Source AI Interview Coach.</title>
      <dc:creator>Harsh Tuli</dc:creator>
      <pubDate>Sun, 04 Oct 2026 10:30:39 +0000</pubDate>
      <link>https://dev.to/452harsh/patternwise-the-ai-coach-that-says-dont-solve-another-problem-yet-59oc</link>
      <guid>https://dev.to/452harsh/patternwise-the-ai-coach-that-says-dont-solve-another-problem-yet-59oc</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Patternwise&lt;/strong&gt; is a DSA and System Design practice workspace with an &lt;strong&gt;AI interview coach&lt;/strong&gt; built on open-weight Gemma models. It answers one question every morning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Given everything I've practised so far, what should I learn, revise, or practise today?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I built it for &lt;strong&gt;Nikhil&lt;/strong&gt;, a friend from my office who recently started preparing for interviews.&lt;/p&gt;

&lt;p&gt;When he started, he jumped between multiple websites and couldn't keep track of his progress across them. Some sites only cover DSA, others only System Design, but today's interviews expect &lt;strong&gt;both&lt;/strong&gt;. And even within one site, a list of hundreds of problems tells you what you've done, not what to do next: &lt;em&gt;Is sliding window still weak? Should I learn something new, or redo what I failed last week?&lt;/em&gt; It never tells you to &lt;strong&gt;stop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So I built one place for both DSA and System Design (HLD and LLD), with an AI coach on top that looks at everything he's practised and tells him what to work on today. Now he can just open it and practise.&lt;/p&gt;

&lt;p&gt;Patternwise does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tracks practice by pattern, not by problem.&lt;/strong&gt; There are 27 topics, 112 patterns and 364 curated LeetCode problems, plus 77 System Design concepts, 35 HLD/LLD patterns and 34 design problems. Every attempt records the result (solved, needed hints, failed), confidence, time, and your own mistake notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computes what matters today&lt;/strong&gt;, with a deterministic recommendation engine. Each pattern gets an explainable priority built from six signals: weakness, revisions due, time since practice, interview importance, variation coverage and recent trend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coaches you through it&lt;/strong&gt; with Gemma. The model explains the plan using your own data, answers "why do I keep failing this?", gives progressive hints without spoiling the solution, checks your explanations, and plays a realistic interviewer that asks follow-up questions instead of teaching.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The feature I'm proudest of: &lt;strong&gt;the coach knows when &lt;em&gt;not&lt;/em&gt; to give you another problem.&lt;/strong&gt; If you keep failing the same idea across different problems, it says so:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't solve another problem yet.&lt;/strong&gt; You've struggled with &lt;em&gt;Variable Window: Longest Valid&lt;/em&gt; on 4 different problems recently. Spend 10 minutes on the invariant, then try one guided problem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the difference between a coach and a problem generator.&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%2Fygdt30b4burv5z2ip553.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%2Fygdt30b4burv5z2ip553.png" alt="Today's plan: the coach says don't solve another problem yet" width="800" height="959"&gt;&lt;/a&gt;&lt;/p&gt;

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

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

&lt;p&gt;🔗 &lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://patternwise.onrender.com" rel="noopener noreferrer"&gt;https://patternwise.onrender.com&lt;/a&gt; · password: &lt;code&gt;patternwise-demo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The demo account has three weeks of realistic practice history: strong hashing, a sliding-window pattern that keeps going wrong, an overdue binary search, and a System Design concept with slipping ratings. That way you can see the coach react to real signals. Try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI Coach → Today's Plan:&lt;/strong&gt; the priority badge shows exactly how each score was calculated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask Coach:&lt;/strong&gt; "Why do I keep struggling with sliding window?" The answer quotes the mistake notes from the history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interview Mode → System Design → Design a URL Shortener:&lt;/strong&gt; answer a few questions, then &lt;em&gt;End &amp;amp; get feedback&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&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%2Fk5q6tnjngqhpm1qtfoli.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%2Fk5q6tnjngqhpm1qtfoli.png" alt="How a priority is calculated" width="800" height="556"&gt;&lt;/a&gt;&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%2Fg10dwahqf4kw30sduidg.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%2Fg10dwahqf4kw30sduidg.png" alt="Interviewer feedback after a mock System Design interview" width="800" height="944"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&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/452Harsh" rel="noopener noreferrer"&gt;
        452Harsh
      &lt;/a&gt; / &lt;a href="https://github.com/452Harsh/patternwise" rel="noopener noreferrer"&gt;
        patternwise
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Pattern-first DSA preparation workspace: curated curriculum, spaced-repetition revision and progress analytics (Next.js + MongoDB)
    &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;Patternwise&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/452Harsh/patternwise/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/452Harsh/patternwise/actions/workflows/ci.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A personal, pattern-first preparation workspace for &lt;strong&gt;DSA&lt;/strong&gt; (data structures and algorithms) and &lt;strong&gt;System Design&lt;/strong&gt; (HLD and LLD). It combines a curated curriculum with a tracker built around recognition, spaced-repetition revision, daily plans, notes, workspaces and analytics, plus an &lt;strong&gt;AI interview coach&lt;/strong&gt; that runs on open-weight models and answers: &lt;em&gt;"Given everything I've practised, what should I do today?"&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It's built with Next.js 16, React 19 and TypeScript, styled with Tailwind CSS v4 and shadcn/ui on Base UI, and stores data in MongoDB. It's a single-user app that you sign in to with a password.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Most practice tracking is a checklist of problems. Patternwise is organised around the &lt;strong&gt;pattern&lt;/strong&gt; instead: how to recognise it, the core idea, a template, variations, pitfalls and where else it applies. Every solve, review and note feeds a spaced-repetition schedule, so things you're shaky on come back before you forget them. System Design gets the…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/452Harsh/patternwise" 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;&lt;strong&gt;Stack:&lt;/strong&gt; Next.js 16 (App Router), React 19, TypeScript, Tailwind v4 and shadcn/ui; MongoDB Atlas (data plus vector search); deployed on &lt;strong&gt;Render&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open-source AI:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Where it runs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Coaching, hints, interviewer&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Gemma 4 26B-A4B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Google AI Studio (production)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same, fully local&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Gemma 4 E2B&lt;/strong&gt; (QAT, 4.3 GB)&lt;/td&gt;
&lt;td&gt;Ollama on my 8 GB M1, about 28 tokens/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieval embeddings&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;EmbeddingGemma&lt;/strong&gt; (768-d)&lt;/td&gt;
&lt;td&gt;Hugging Face Inference (production) or Ollama (local)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The rule that shaped everything: the engine decides, the model explains
&lt;/h3&gt;

&lt;p&gt;Small open models are good at explanation and dialogue. They're bad at arithmetic over a user's history, and they'll happily invent "you've solved 12 sliding-window problems". So I split the work:&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%2Fgcoz5f1ihn1gj93n8o8c.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%2Fgcoz5f1ihn1gj93n8o8c.png" alt="How Patternwise works: the engine decides, Gemma explains." width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;The engine&lt;/strong&gt; (&lt;code&gt;lib/coach/&lt;/code&gt;) is plain, pure TypeScript and fully tested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For each pattern it computes success over the last 21 days (hints count half), failures, confidence, due reviews, staleness, per-difficulty results, and variation coverage. Every curated problem teaches one variation, so "10 solved but only 1 variation" is detectable.&lt;/li&gt;
&lt;li&gt;From those facts it picks an action (&lt;em&gt;review the concept, revise, strengthen, broaden, level up, learn&lt;/em&gt;) and a difficulty move (&lt;em&gt;harder, easier, back to fundamentals&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;For System Design it also spots concepts you &lt;strong&gt;studied but never applied&lt;/strong&gt; in a design problem.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;stop rule&lt;/strong&gt; fires on 3+ shaky attempts across 2+ problems with mostly failed recent attempts. Then it recommends one guided problem, not five more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Gemma&lt;/strong&gt; gets those facts as structured text, plus retrieved curriculum. It runs under strict rules: every user-specific claim must come from the supplied data, and if the data doesn't say, it says it doesn't know. The server recomputes all facts from MongoDB on every request, so the browser can't feed the model made-up stats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RAG on MongoDB Atlas Vector Search:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Indexing:&lt;/strong&gt; 649 curriculum documents are embedded with EmbeddingGemma (patterns with signals, templates and pitfalls; problems; HLD/LLD concepts, patterns and problems). They live in a &lt;code&gt;knowledge&lt;/code&gt; collection with a &lt;code&gt;knowledge_vector&lt;/code&gt; index filtered by module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search quality:&lt;/strong&gt; retrieval maps unseen problems to the right pattern. "Longest substring without repeating characters" lands on &lt;em&gt;Variable Window&lt;/em&gt;, and "sorted rotated array minimum" lands on &lt;em&gt;Rotated Binary Search&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallbacks:&lt;/strong&gt; if the Atlas index isn't ready it falls back to in-memory cosine, and if the embedding model is down it falls back to keyword search. The coach never just breaks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DSA and System Design stay separate.&lt;/strong&gt; Every coach request is scoped to one module, so a System Design answer never drags in your DSA history, and vice versa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It closes the loop.&lt;/strong&gt; Interview results, "explain it back" scores, and practice logged anywhere in the app feed straight back into tomorrow's plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Things I had to solve:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4's inline reasoning:&lt;/strong&gt; on Google's endpoint it always streams its reasoning as &lt;code&gt;&amp;lt;thought&amp;gt;…&amp;lt;/thought&amp;gt;&lt;/code&gt;, and switching that off isn't supported for this model. I wrote a small stream filter that drops the block, even when a tag is split across chunks, and adds token headroom so the answer isn't cut short.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Different embedding vectors:&lt;/strong&gt; EmbeddingGemma from Ollama and from Hugging Face produce different vectors for the same text (cosine ≈ 0.71). The indexer records which embedding model built the index and flags "index needed" when the provider changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing the engine:&lt;/strong&gt; 34 checks cover the stop rule, revision priority, difficulty moves, time budgets, and that SD data never leaks into DSA. They run in &lt;strong&gt;GitHub Actions CI&lt;/strong&gt; on every push, alongside lint, type-checking, curriculum integrity checks and the production build.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;For an interview coach, open models aren't a philosophy point. They decide &lt;strong&gt;who can use it&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It runs on a laptop, for free.&lt;/strong&gt; The exact same coach runs on Gemma 4 E2B through Ollama on an 8 GB MacBook Air, with EmbeddingGemma for search and MongoDB on localhost: no API keys, no bill. A config switch moves it over, and I checked that while it answers, the app, database and model make &lt;strong&gt;zero connections outside localhost&lt;/strong&gt; (it's in the video at 2:45). Anyone who can't afford a paid prep subscription gets the same coach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your preparation data stays yours.&lt;/strong&gt; Your failures, mistake notes and mock-interview debriefs are personal. With a local model they never leave your machine. Even in the hosted version, the vector store holds &lt;strong&gt;only curriculum content&lt;/strong&gt;; progress and conversations are never indexed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swapping models is a config change, not a rewrite.&lt;/strong&gt; All model calls go through a tiny provider interface. I developed on Gemma 4 E2B locally, deployed on Gemma 4 26B-A4B, and could point it at a GPU server running E4B or 31B by changing two environment variables. No vendor lock-in, no rewrite when pricing or terms change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The open model made the honest design possible.&lt;/strong&gt; Because I control the model and the prompt pipeline end to end, I could put a deterministic engine in charge and give Gemma a clear, checkable job. That's why every number on the screen has an explanation behind it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open models keep it free to run.&lt;/strong&gt; Open Gemma on free tiers, MongoDB Atlas and Render keep running costs at about zero. A closed API would turn a personal study tool into a monthly bill.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What Nikhil said&lt;/strong&gt; after trying it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Honestly, I really liked the concept behind [Patternwise]. Instead of just solving hundreds of random DSA questions, it focuses more on understanding the patterns. I feel that once you know the pattern, you can approach and solve a lot of different questions on your own, rather than just remembering solutions.&lt;/p&gt;

&lt;p&gt;I also liked the way the preparation is structured with a proper timeline, so it's easier to know what to focus on and when. The System Design part is also really helpful because it covers both LLD and HLD. Overall, I think it's a much more practical way to prepare for interviews instead of just going through a huge list of problems."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's exactly the shift I was going for: from &lt;em&gt;how many problems have I solved?&lt;/em&gt; to &lt;em&gt;which patterns do I actually understand?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;I built Patternwise with an AI coding agent, &lt;strong&gt;Claude Code&lt;/strong&gt;, as my pair programmer. I set the direction: what Nikhil needed, the features, the design decisions, and what to cut. I also tested every step in the browser. The agent wrote and refactored most of the code with me, ran the checks, and debugged the issues we hit along the way, like Gemma 4's inline reasoning tags and the embedding mismatch between Ollama and Hugging Face.&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; Gemma 4 26B-A4B powers all coaching, hints and interviews in production; Gemma 4 E2B runs the same app fully locally; EmbeddingGemma powers retrieval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render:&lt;/strong&gt; the whole app (the coach's front end and API) is deployed on Render as a Blueprint (&lt;code&gt;render.yaml&lt;/code&gt;). The repo also includes an optional Render Blueprint that self-hosts Gemma via Ollama behind a key-checking proxy (&lt;code&gt;deploy/render-ollama&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of MongoDB Atlas:&lt;/strong&gt; Atlas is the data layer for all progress, sessions and plans, and &lt;strong&gt;Atlas Vector Search&lt;/strong&gt; retrieves the curriculum for every grounded answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs:&lt;/strong&gt; the demo video's narration is generated with ElevenLabs (voice "Eric"), split per scene and timed to the footage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of GitHub Copilot:&lt;/strong&gt; GitHub Actions CI runs lint, type-checking, curriculum integrity, the 34 coach-engine checks and the production build on every push, plus a weekly job that re-verifies all 364 LeetCode links.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
      <category>hacktoberfest</category>
    </item>
  </channel>
</rss>
