<?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: Tharun</title>
    <description>The latest articles on DEV Community by Tharun (@tharunpandya).</description>
    <link>https://dev.to/tharunpandya</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%2F4166664%2F960d16c7-1fc4-44bb-8bda-c95046d02428.jpg</url>
      <title>DEV Community: Tharun</title>
      <link>https://dev.to/tharunpandya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tharunpandya"/>
    <language>en</language>
    <item>
      <title>The Rules Decide, Gemma Explains: A Garden Planner That Sends You Outside</title>
      <dc:creator>Tharun</dc:creator>
      <pubDate>Sun, 11 Oct 2026 22:07:43 +0000</pubDate>
      <link>https://dev.to/tharunpandya/the-rules-decide-gemma-explains-a-garden-planner-that-sends-you-outside-16bf</link>
      <guid>https://dev.to/tharunpandya/the-rules-decide-gemma-explains-a-garden-planner-that-sends-you-outside-16bf</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;In cricket, the third umpire watches the replay and gives the decision: out or not out. The commentators explain why, in friendly words. Nobody ever lets the commentator give the batter out.&lt;/p&gt;

&lt;p&gt;That one rule is the whole design of &lt;strong&gt;GrowWise&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;GrowWise is a garden planner for anyone with a balcony, a few pots, a sunny windowsill or a small plot, who wants less time on the phone and more time with their hands in soil. You tell it what kind of space you have, how much sun it gets and how big it is. Then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plain rules decide&lt;/strong&gt; which of 12 plants can actually live in your space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every "no" comes with a reason&lt;/strong&gt;, in words a beginner can read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You get a four-week plan measured in minutes outdoors&lt;/strong&gt;, not minutes in the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A small open-weight model, Gemma 2 2B, can explain the plan&lt;/strong&gt; in friendly language, running on your own laptop. If no model is running, everything still works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model is the commentator. The rules are the umpire.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it gets you off the screen
&lt;/h3&gt;

&lt;p&gt;The output is not a chat. It is a to-do list for your hands. Most apps want you to scroll longer. GrowWise wants you to close the tab.&lt;/p&gt;

&lt;p&gt;For a 12 sq ft balcony with partial sun, GrowWise picks four plants: French breakfast radish (a small cousin of mooli), baby spinach (palak), peppermint (think pudina) and butterhead lettuce. It then asks for &lt;strong&gt;340 minutes outside over four weeks&lt;/strong&gt;: 100 in week one, 60 in week two, 80 in week three and 100 in week four. Every task lists the supplies you need and a safety note. A tracker adds up the minutes you log outdoors. It never measures how long you spend looking at the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where it says no
&lt;/h3&gt;

&lt;p&gt;You know how a food delivery app sometimes says you are outside its service area, and nothing else? That feeling of being told no without a reason is what I wanted to avoid. In GrowWise, every rejected plant explains itself:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your setup&lt;/th&gt;
&lt;th&gt;Plant&lt;/th&gt;
&lt;th&gt;What GrowWise tells you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Balcony, partial sun&lt;/td&gt;
&lt;td&gt;Cherry tomato&lt;/td&gt;
&lt;td&gt;Insufficient sunlight: Requires 6.5h direct sun, but your space receives partial_sun (~4.5h).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backyard bed&lt;/td&gt;
&lt;td&gt;Peppermint&lt;/td&gt;
&lt;td&gt;Excluded from in-ground plots: Peppermint root rhizomes spread aggressively and invade beds.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Anyone who has planted pudina straight into the ground knows why that second rule exists. And on a dim kitchen windowsill (indoor, shade, 4 sq ft), exactly one of the twelve plants passes: baby spinach.&lt;/p&gt;

&lt;p&gt;GrowWise is built for beginners with small spaces. These are the people who buy a plant, watch it die and decide they have no green thumb, when often the plant was never going to survive that spot.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Live app: &lt;a href="https://growwise-84y9.onrender.com/" rel="noopener noreferrer"&gt;growwise-84y9.onrender.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.mux.com/cWSKIQiKHx00CYlcJz02jXLS7X6mqTYXWzr4OHqnWclK4" width="710" height="399"&gt;
&lt;/iframe&gt;

&lt;/p&gt;

&lt;h2&gt;
  
  
  I Took It Outside
&lt;/h2&gt;

&lt;p&gt;I have a small garden at home in West Godavari, in Andhra Pradesh. Some time ago I planted a plant there. After some days, it died.&lt;/p&gt;

&lt;p&gt;A dying plant does not send a WhatsApp message saying "too little sun, please help". It just dies, and you are left guessing. If you have ever watched a plant fail and decided you have no green thumb, you know that feeling.&lt;/p&gt;

&lt;p&gt;Then I built GrowWise, and I typed in my own garden:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Location:&lt;/strong&gt; West Godavari&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Garden:&lt;/strong&gt; a small home garden&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sunlight:&lt;/strong&gt; partial sun&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The answer was clear. Eight of the twelve plants failed the sunlight check the moment I chose partial sun, and every one of them came with a reason in plain words. They want five to six and a half hours of direct sun, and GrowWise counts partial sun as about four and a half. Only a small handful of plants passed, and spinach (palak) and lettuce were on that list.&lt;/p&gt;

&lt;p&gt;That is when I understood why my plant died. It was not bad luck, and my garden was not cursed. The plant and the spot never matched, and I could not see that until GrowWise showed me.&lt;/p&gt;

&lt;p&gt;GrowWise also got something wrong, and I want to be honest about it. It ignored "West Godavari". The rules only use the garden type, the sunlight and the size, so it cannot yet tell me whether this is the right season to plant. That is the next thing I want to fix.&lt;/p&gt;

&lt;p&gt;GrowWise did not bring my plant back. It gave me something more useful: the reason, and a list of plants that can really live in my garden. From now on I choose the plant after I know my spot, not before.&lt;/p&gt;

&lt;p&gt;The screen gave me the answer. The rest of the work happens in the soil.&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/tharun-pandya" rel="noopener noreferrer"&gt;
        tharun-pandya
      &lt;/a&gt; / &lt;a href="https://github.com/tharun-pandya/GrowWise-" rel="noopener noreferrer"&gt;
        GrowWise-
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Local garden planner
    &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;🌱 GrowWise: Grounded AI-Powered Garden Planner&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://hacktoberfest.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/127bb4bd4c1f103c15e00265ea48d9559a78385518ea956ca4394dd4aa9badde/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f62657266657374253230323032362d5765656b25323031253341253230546f75636825323047726173732d3265613434662e737667" alt="Hacktoberfest 2026"&gt;&lt;/a&gt;
&lt;a href="https://react.dev" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/34d9985ee073274f02d4df5e0e5e35db8b7aee17b52cd686636c16c7f38fb861/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f46726f6e74656e642d526561637425323025324225323054797065536372697074253230253242253230566974652d3631646166622e737667" alt="Frontend"&gt;&lt;/a&gt;
&lt;a href="https://fastapi.tiangolo.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a6b7fc9723cc335e3254f40dfb22e2be66d6103be64d2a4da347011cd83cdbb0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4261636b656e642d507974686f6e253230466173744150492d3030393638382e737667" alt="Backend"&gt;&lt;/a&gt;
&lt;a href="https://ai.google.dev/gemma" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/52f08cfc438dfd275d2cb890232c7f13c661c8d9d8d91b3d8e97b42ae1d0490d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f726525323041492d47656d6d612532303225323032422d626c756576696f6c65742e737667" alt="Core AI"&gt;&lt;/a&gt;
&lt;a href=""&gt;&lt;img src="https://camo.githubusercontent.com/4ee6b04546a90ca3f88d2ad6b108e5bfb0f7e9fb72982f0f9066f585322b0309/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f54657374732d323325323050617373696e672d627269676874677265656e2e737667" alt="Tests"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A lightweight, transparent, and grounded garden planner built for the &lt;strong&gt;Hacktoberfest 2026 Open-Source AI Challenge, Week 1: Touch Grass&lt;/strong&gt;
Built exclusively with &lt;strong&gt;React + TypeScript + Vite&lt;/strong&gt; on the frontend and &lt;strong&gt;Python FastAPI&lt;/strong&gt; on the backend. Designed for deployment on &lt;strong&gt;Render&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🌟 Key Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Modern React + TypeScript + Vite Frontend&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Nature-inspired design system with responsive desktop and mobile layouts.&lt;/li&gt;
&lt;li&gt;Interactive garden profile with 8 validated growing conditions.&lt;/li&gt;
&lt;li&gt;4-Week actionable outdoor task planner with session tracking and Markdown export.&lt;/li&gt;
&lt;li&gt;Transparent disqualification logs: see exactly why a crop failed without black-box surprises.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Botanical Rules Engine&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Sunlight, container depth, soil requirements, and USDA Hardiness Zones evaluated with strict boolean logic.&lt;/li&gt;
&lt;li&gt;Sourced from 12 authoritative agricultural extension guides (USDA, Cornell, Penn State, UMN, UC Davis, Purdue, etc.).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-Weight Gemma 2 2B Architecture&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Primary AI: &lt;strong&gt;Gemma 2 2B&lt;/strong&gt; running via Ollama or…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tharun-pandya/GrowWise-" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The repo in five lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data/                    crops.json and gardening_sources.json (12 crops, 12 sources)
backend/app/             FastAPI: rules engine, model provider, safety check
src/                     React + TypeScript + Vite front end (the rules run here too)
tests/ backend/tests/    23 unit tests
render.yaml, build.sh    one-service deploy on Render
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run 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;git clone https://github.com/tharun-pandya/GrowWise-.git
&lt;span class="nb"&gt;cd &lt;/span&gt;GrowWise-
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev        &lt;span class="c"&gt;# the app runs on http://localhost:3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; unittest backend/tests/test_backend.py tests/test_knowledge_base.py tests/test_rules.py tests/test_ai_engine.py &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last lines of the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ran 23 tests in 0.036s

OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The numbers first: &lt;strong&gt;12 crops, 12 sources, 23 tests, 120 gardens cross-checked, 340 outdoor minutes in the sample plan, a model file under 2 GB, $0 to run.&lt;/strong&gt; Now the story.&lt;/p&gt;

&lt;h3&gt;
  
  
  The idea in one picture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; your answers (garden type, sun, size)
                 |
                 v
  RULES (plain if-statements, 12 crops)    &amp;lt;- the umpire decides
                 |
                 +--&amp;gt; plants that pass, with a reason for every "no"
                 v
  PROMPT built only from the plants that passed
                 |
                 v
  EXPLAINER                                &amp;lt;- the commentator talks
     offline template   (no model, always works)
     or Gemma 2 2B      (open-weight, runs on your machine)
                 |
                 v
  4-week plan in minutes outdoors --&amp;gt; you go outside
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: the rules, as a plain loop
&lt;/h3&gt;

&lt;p&gt;Before any AI, a garden planner has to answer a boring question: can this plant live here? That is not a language problem. It is three comparisons. Does the plant suit this kind of garden? Is there enough sun? Is there enough room?&lt;/p&gt;

&lt;p&gt;Your sunlight answer becomes hours: full sun counts as 7, partial sun as 4.5 and shade as 2.5. Each crop has a minimum, with a small allowance of a quarter hour (half an hour for shade-loving plants). Here is the heart of it, trimmed to the essentials:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;SUN_HOURS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;full_sun&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;7.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;partial_sun&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shade&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_crop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;garden_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sunlight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;space_sq_ft&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;reasons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;                                    &lt;span class="c1"&gt;# why this crop is rejected
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;garden_type&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;compatible_garden_types&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not suitable for a &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;garden_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&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;garden_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;backyard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;peppermint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Peppermint spreads underground and takes over the bed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;sun_you_have&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SUN_HOURS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sunlight&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;tolerance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sunlight&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shade&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sun_you_have&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min_sunlight_hours&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="n"&gt;tolerance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Needs &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;min_sunlight_hours&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;h of sun, you get about &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sun_you_have&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;h.&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;space_sq_ft&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min_space_sq_ft&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Needs &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;min_space_sq_ft&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; sq ft, you have &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;space_sq_ft&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reasons&lt;/span&gt;                                  &lt;span class="c1"&gt;# empty list means: grow it
&lt;/span&gt;
&lt;span class="n"&gt;crops&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/crops.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;crop&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;crops&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;reasons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;check_crop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;balcony&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;partial_sun&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&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;reasons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NO &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;common_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YES&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;common_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running it on a 12 sq ft balcony with partial sun:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NO  Sweet Basil -&amp;gt; Needs 6.0h of sun, you get about 4.5h.
NO  Cherry Tomato -&amp;gt; Needs 6.5h of sun, you get about 4.5h.
YES Butterhead Lettuce
YES Baby Spinach
YES Peppermint
YES French Breakfast Radish
NO  Bell Pepper -&amp;gt; Needs 6.5h of sun, you get about 4.5h.
NO  Danvers Half-Long Carrot -&amp;gt; Not suitable for a balcony. | Needs 6.0h of sun, you get about 4.5h.
NO  Bush Green Bean -&amp;gt; Needs 6.0h of sun, you get about 4.5h.
NO  Tuscan Rosemary -&amp;gt; Needs 6.5h of sun, you get about 4.5h.
NO  Alpine Strawberry -&amp;gt; Needs 5.0h of sun, you get about 4.5h.
NO  French Marigold -&amp;gt; Needs 6.0h of sun, you get about 4.5h.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real engine in the repo also adds a score and a "why it passed" list, but the pass or fail logic is exactly this.&lt;/p&gt;

&lt;p&gt;The same rules live in two places: Python (the API) and TypeScript (your browser). I compared them on 120 gardens: 5 garden types, 3 sunlight levels and 8 sizes. They picked the same crops every time. The browser copy means the planner keeps working after the page loads, even if your connection drops.&lt;/p&gt;

&lt;p&gt;The facts live in &lt;code&gt;data/crops.json&lt;/code&gt;, and each crop points to a source in &lt;code&gt;data/gardening_sources.json&lt;/code&gt;: USDA Cooperative Extension, Cornell, Penn State, the University of Minnesota, the Royal Horticultural Society, Purdue, the University of Florida, Oregon State, Iowa State, Clemson and NC State.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: a small open model with a small job
&lt;/h3&gt;

&lt;p&gt;An &lt;strong&gt;open-weight model&lt;/strong&gt; is one whose trained weights you can download and run yourself. &lt;strong&gt;Ollama&lt;/strong&gt; is a free app that runs such models on your own computer. I used Gemma 2 2B (&lt;code&gt;gemma2:2b&lt;/code&gt;). The file is under 2 GB, so it fits on an ordinary laptop, and I designed GrowWise for machines with 8 GB of RAM or less.&lt;/p&gt;

&lt;p&gt;When you pick &lt;em&gt;Local Gemma 2 2B (Ollama Endpoint)&lt;/em&gt; on the Weekly Plan page, your browser talks straight to Ollama on your own machine, at &lt;code&gt;localhost:11434&lt;/code&gt;, using the OpenAI-style chat format that Ollama offers. Nothing goes through my server. This is what it sends, shortened to two of the four crops:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST http://localhost:11434/v1/chat/completions
model: gemma2:2b        temperature: 0.2

system:
You are GrowWise, a grounded horticultural AI assistant for Hacktoberfest Touch Grass. Never hallucinate botanical facts. Rely strictly on provided context.

user:
GARDEN PROFILE:
Location: Pune
Garden Type: balcony
Sunlight: partial_sun
Area: 12 sq ft
Experience: beginner

VERIFIED SUITABLE CROPS:
- French Breakfast Radish (Raphanus sativus): Requires 4.5h sun, 0.2 sq ft, 6" root depth. Matures in 25 days. Guidance: Thin seedlings early to 2 inches apart to allow crisp root bulbing. Keep watering steady.
- Baby Spinach (Spinacia oleracea): Requires 3h sun, 0.5 sq ft, 6" root depth. Matures in 38 days. Guidance: Keep soil evenly damp. Does exceptionally well in dappled sunlight or balcony shade.
(two more crops in the same format)

TASK:
Explain why these crops suit this layout and provide 3 practical outdoor next steps to get outside and touch grass.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things to notice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The model only sees plants that already passed. It cannot add a crop. It cannot remove one. The worst it can do is explain badly.&lt;/li&gt;
&lt;li&gt;The temperature is 0.2. A low temperature means less random wording.&lt;/li&gt;
&lt;li&gt;You can read the exact prompt yourself. The &lt;strong&gt;Inspect Grounded Prompt&lt;/strong&gt; button shows it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Can a 2B model still write a clumsy sentence? Of course. That is exactly why I never ask it to pick plants or to remember plant facts. A model this small is too small to trust with them. The facts come from a file, they are pasted into the prompt, and the model only does the talking.&lt;/p&gt;

&lt;p&gt;The API also has an extra safety net. If you ask the server to explain a plan with a model, the answer goes through a small checker first. It looks for products I do not want near a beginner's balcony, like Roundup, glyphosate and bleach, and for medical claims like "cures cancer". One of the 23 tests hands it the sentence "Apply roundup and synthetic pesticide across the soil bed" and checks that it gets blocked. It is a blunt word list, not magic.&lt;/p&gt;

&lt;p&gt;To try Gemma yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma2:2b     &lt;span class="c"&gt;# one-time download, under 2 GB&lt;/span&gt;
npm run dev               &lt;span class="c"&gt;# then choose "Local Gemma 2 2B" on the Weekly Plan page&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your browser blocks the call to Ollama, add the page's address to Ollama's &lt;code&gt;OLLAMA_ORIGINS&lt;/code&gt; setting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: when there is no model
&lt;/h3&gt;

&lt;p&gt;A model should never be the single point of failure in a garden app. So there is an &lt;strong&gt;offline explainer&lt;/strong&gt;: a template that fills in the same verified facts with no model at all. It is also what the live demo uses by default, because a free Render instance has only 512 MB of memory, far too little for a language model. If you choose Gemma and Ollama is not running, the app says so in one line and shows the verified care notes instead. No error page, no spinner that never ends.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: the part for your hands
&lt;/h3&gt;

&lt;p&gt;The plan runs four weeks, with one set of tasks per crop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Week 1: setup and planting, 25 minutes&lt;/li&gt;
&lt;li&gt;Week 2: a moisture check and thinning seedlings, 15 minutes (about one chai break)&lt;/li&gt;
&lt;li&gt;Week 3: mulching and a pest check, 20 minutes&lt;/li&gt;
&lt;li&gt;Week 4: pruning and the first harvest, 25 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every task lists supplies and a safety note. They are simple templates, the same shape for every crop. I would rather give you a plan that is easy to follow than a clever one you ignore. You can export it as Markdown, and the &lt;strong&gt;Outdoor Progress&lt;/strong&gt; page logs the minutes you actually spend outside. Your plan and your minutes live in your browser's &lt;code&gt;localStorage&lt;/code&gt;. No account, no copy on my server, and no tracking code anywhere in the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: shipping it on Render
&lt;/h3&gt;

&lt;p&gt;GrowWise is one Render web service on the free plan. FastAPI serves the built React app and the API from a single URL, so there is no CORS puzzle and nothing to keep in sync. &lt;code&gt;render.yaml&lt;/code&gt; describes the service. &lt;code&gt;build.sh&lt;/code&gt; builds the front end, installs the Python packages and then runs all 23 tests, so if a rule breaks, the build fails and the broken version never goes live. A &lt;strong&gt;Run Test Suite (23 Tests)&lt;/strong&gt; button in the top bar runs the same suite on the server and shows the output, so anyone can check the claims in this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it does not do yet
&lt;/h3&gt;

&lt;p&gt;I would rather tell you than have you find out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It does not use your location yet.&lt;/strong&gt; The form asks where you are, but only to label your plan. The rules look at garden type, sunlight and area. The crop data already carries hardiness zones, but the rules do not read them. So no frost dates and no "what to plant this week" yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The database is small.&lt;/strong&gt; Twelve crops, mostly vegetables and herbs from US and UK guides. A balcony in Pune and a balcony in Seattle get the same rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The live demo uses the offline explainer.&lt;/strong&gt; Gemma needs your own Ollama.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The safety word list is not in the browser path yet.&lt;/strong&gt; The Gemma button talks to Ollama directly, so it does not pass through the checker on the API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rules protect the plants, not the prose.&lt;/strong&gt; A 2B model can still write a clumsy paragraph. I did not benchmark any model, so everything here is a design argument, not a leaderboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is next
&lt;/h3&gt;

&lt;p&gt;Zones, frost dates and seasons, so GrowWise can answer "what do I plant this week?". Crops that people actually grow in Indian homes, like tulsi, methi, coriander and genda. And the safety check inside the browser path. In the next post I will add those to the rules. The model will still not be allowed near them.&lt;/p&gt;

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

&lt;p&gt;The challenge asks where an open approach beat a closed one. Here are straight answers, including the parts where I have no proof.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it work without internet?&lt;/strong&gt; Mostly, yes, and a hosted model could not. After the page loads, the rules, the plan and the offline explainer all run in your browser, so you can plan on a balcony with no signal. Run GrowWise locally with Ollama and the model runs on your laptop too. A hosted model needs a connection for every sentence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whose server sees my garden?&lt;/strong&gt; Nobody's. In offline mode and in Gemma mode, your answers never leave your browser and your own machine. The profile and tracker sit in &lt;code&gt;localStorage&lt;/code&gt;, and there are no accounts. With a closed API, every prompt would travel to a company's server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I swap the model?&lt;/strong&gt; On the server, yes, with two settings. The model provider reads its endpoint and model name from environment variables (&lt;code&gt;OPEN_WEIGHT_ENDPOINT_URL&lt;/code&gt; and &lt;code&gt;OPEN_WEIGHT_MODEL_NAME&lt;/code&gt;) and speaks the chat format that Ollama, vLLM and many hosts understand. Today it is &lt;code&gt;gemma2:2b&lt;/code&gt;. Tomorrow it can be a bigger Gemma or a different open model, and the rules do not change. The browser button is fixed to Ollama and &lt;code&gt;gemma2:2b&lt;/code&gt; for now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it cost anything?&lt;/strong&gt; No. A free Render plan, no API key and no per-token bill. The offline explainer is free, and Gemma on my own laptop costs electricity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where did open work better?&lt;/strong&gt; I did not test a closed model, and a big hosted one would probably write a prettier paragraph. The real win was the design. Because Gemma 2 2B is too small to trust with facts, I never asked it to pick plants or remember any. The rules decide, the model explains, and the app still works with the model switched off. A bigger hosted model makes it very easy to skip that discipline, and a garden app that confidently tells a beginner the wrong thing about mint is worse than no app.&lt;/p&gt;

&lt;p&gt;An open model will not always write the nicest sentence. It does let me decide where the sentence is written, what it is allowed to say and who gets to see it. For a garden app, that was the better trade.&lt;/p&gt;

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

&lt;p&gt;I built GrowWise in Google AI Studio, using it as my AI coding assistant to design the rules engine, the TypeScript types and the unit tests. It is a development tool. The deployed app does not call it or any other closed AI service.&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 2 2B (&lt;code&gt;gemma2:2b&lt;/code&gt;) is the open-weight model behind the explainer, run locally through Ollama. The server side can point at any OpenAI-compatible Gemma endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render:&lt;/strong&gt; The whole app, front end and API, runs as one free Render web service, deployed from &lt;code&gt;render.yaml&lt;/code&gt;, with a health check and a build that runs the tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading. Now close the laptop and go touch some grass.&lt;/p&gt;

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