DEV Community

Karan Raj
Karan Raj

Posted on

I built my mother a planting calendar that tells her what to sow this week

Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass Submission 🌿

This is a submission for the Hacktoberfest Week 1 Challenge: Touch Grass

What I Built

I built Sprout, a planting calendar that tells you what you can put in the ground this week, then gets out of the way.

My mother has a terrace she plants on every October. She does not need an app that tells her to "prepare for the season." She needs three lines and then her hands in the soil. Every planting website I looked at wanted an account, or a dropdown for her exact city, and then showed me an advert for seed companies.

The question itself is simpler than all that. Almost every cool-season vegetable is timed backwards from the first expected frost. Spinach tolerates about -6C, so it goes in roughly two months before frost and stays open until about a week before. Once you know your frost date, the rest is subtraction.

Sprout knows seven regions' frost dates. You ask it, it tells you what is open, what is not yet, and what window you have missed - then it explains it in a sentence and stops.

Demo

A real run, qwen2.5:3b on a CPU with no GPU:

==================================================================
  Sprout - Friday 09 October 2026
  North India - Delhi, Punjab, Haryana
  first frost expected 20 November  (42 days away)
==================================================================

Sow now
  Broad beans   direct          to -4C    (window closes 11 Oct)
  Peas          direct          to -3C    (window closes 16 Oct)
  Spinach       direct          to -6C    (window closes 15 Nov)
  Kale          direct          to -10C   (window closes 20 Nov)
  Carrots       direct          to -5C    (window closes 31 Oct)
  Onions        start in trays  to -5C    (window closes 21 Oct)

Not yet - sow on these dates
  Cabbage       start in trays  to -8C    (in 2 days, from 11 Oct)
  Garlic        direct          to -12C   (in 30 days, from 08 Nov)

Window has closed, next season
  ...
Enter fullscreen mode Exit fullscreen mode

Then the part the model is actually for:

Get your broad beans in this week, while the ground is still workable -
they fix their own nitrogen, so whatever you plant after them will thank
you for it. Radishes are the fastest thing on this list if you want to see
something up quickly.
NEXT: Should I soak the broad bean seeds before sowing them?
Enter fullscreen mode Exit fullscreen mode

And when I asked it something it should not have had an opinion about:

$ sprout.py --ask "why not sow tomatoes now?"

Tomatoes aren't open for sowing in your region at the moment. They're a
warm-season crop and there isn't room for them before the last frost in
spring. Right now, radish, spinach and lettuce are the quickest things to
get into the ground.
Enter fullscreen mode Exit fullscreen mode

Code

github.com/karan244/sprout - MIT licensed.

No pip install. Standard library only, so there is no requirements.txt and no virtualenv. You need Ollama and a model, and that is the entire setup.

./run.sh                  # today, your region
./run.sh --region uk      # 7 regions, or --list to see them
./run.sh --no-ai          # facts only, instant, no model involved
Enter fullscreen mode Exit fullscreen mode

How I Built It

The model is Qwen2.5:3B running locally through Ollama. It does exactly one job, and it is not the important one.

The dates are arithmetic, so the dates are in code

Every crop carries a sowing window measured in days relative to the first frost. spinach: -60 to -5 means sow from 60 days before frost until 5 days before it. Each region carries an approximate frost date. Deciding what is open today is then a date comparison: deterministic, instant, and identical every single run.

The model only explains

This split is the entire design, and I got it wrong twice before getting it right.

First attempt. I handed the model the whole computed crop list and told it not to repeat it. It repeated it anyway, in slightly different words, as though that counted as advice.

The fix was to stop giving it something to echo. It now receives only what it could not already know: the region, the frost date, and the planting-guide notes attached to whichever crops are currently open. Those notes are real horticultural knowledge - "broad beans fix their own nitrogen, so they feed the soil behind them" - that a 3B model would not reliably have. Give it that instead of a list and it has something to add.

Second bug, and a worse one. The question mode had no grounding at all. I asked it "why not sow tomatoes now?" and it answered:

It simply agreed with me, then attached confident detail. Asking a language model a question about a system it cannot see gets you agreement, not an answer. It now receives the same three computed lists as the plan, with an explicit instruction never to advise sowing something they show as closed. That question now comes back as "Tomatoes aren't open in your region at the moment", with a suggestion of what is.

A southern hemisphere bug worth mentioning

Melbourne's first frost is in April. My original date code read that as a date that had already passed this year and printed:

first frost expected 25 April (-167 days away)
Enter fullscreen mode Exit fullscreen mode

...then cheerfully reported that every crop's window had closed. Frost dates now roll forward to the correct year, which for the southern hemisphere means the next one.

Things it does not do

  • It does not decide anything. Every date in the plan comes from the code. If the model is wrong it can only make the explanation worse, never the advice wrong.
  • It is cool-season only. Tomatoes, peppers and aubergines are timed against the last frost, which is a different calculation.
  • Frost dates are regional averages, deliberately rounded, not a forecast. --first-frost overrides them.
./selftest.sh          # 11 checks, about 40 seconds
Enter fullscreen mode Exit fullscreen mode

Two of those checks exist because they caught real bugs: the frost roll-forward above, and the hallucinated-tomato case, kept as a regression test so it cannot come back quietly.

Why Does Open Innovation Matter?

The obvious reason is the same as for any local model, and it is not the interesting one here. The interesting one is that this project is mostly not an AI project at all.

Almost all of Sprout is arithmetic - frost dates, offsets, comparisons - and that part would be identical if no model existed. Which is exactly why it is better that way. Sowing dates are the kind of thing you do not want a language model improvising at 8am on a terrace. A 3B model will confidently tell you to plant tomatoes in October because you asked it nicely. Ordinary code cannot be talked into being wrong like that, and it costs nothing, takes no time, and works with the network off.

So the division of labour became: let the code hold anything a beginner would act on, and let the model hold anything that benefits from sounding like a person. The dates are arithmetic. "Get the broad beans in while the ground is still workable, and radishes if you want to see something up fast" is conversation - and that is the half a rulebook cannot do.

The privacy argument is real too, and pointed in an unexpected direction. A gardening question is not sensitive in the way a language-practice mistake is. But once a tool starts talking to a hosted model, everything you type goes with it, and I did not want a tool on my mother's terrace reporting her growing plans to a third-party logging pipeline. Running the model locally means I can hand her something without a privacy conversation attached, and it costs nothing to run forever.

Concretely: Qwen2.5:3B on a CPU answers in a few seconds, costs nothing per query, works with the network disconnected, and sent zero bytes off the machine during the whole demo above. The open-weight model was the only way to get the conversational half of this at all, and the open licence on the weights is why I could put it in something I would actually hand to my mother.

Licence

MIT. Model weights are Alibaba's Qwen, distributed under the Apache 2.0 licence.


Built with AI coding assistance. I made every design decision, chose the model, wrote and ran every line shown above, and every transcript in this post is a real capture rather than an illustration.

Top comments (0)