This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass
October is the heart of the rainy season in Panama City. Most afternoons, the sky opens up somewhere between 1 and 5 p.m. A weather app gives me a percentage for every hour and leaves the decision to me, so I end up checking the screen over and over, or I just stay inside.
Ventana seca ("dry window") answers one question: when today can I be outside without getting soaked or cooked?
I don't have a free weekday morning for a trail. My chance to be outside is my daily route: on foot near Plaza 5 de Mayo at 7:30 a.m., arriving in Costa del Este at 8:00, lunch at noon, heading out at 4:00. So the main mode takes that route, checks the forecast for each stop, and tells me which stop is worth spending time outdoors, plus what to carry for the whole day. On weekends, a second mode looks for the best window at a park like the Parque Natural Metropolitano or the Cinta Costera.
You run one command in the morning, read one card, and optionally save a calendar event that alerts you 30 minutes before. Then the phone goes back in your pocket. The screen should be the shortest part of the experience.
This is my real route for Tuesday, October 6, with the real forecast and Gemma choosing on my laptop:
The 7:30 and 8:00 stops look dry (20% and 18%). Noon brings a 66% chance of rain with a 39 °C heat index, and 4:00 p.m. is at 88%. Gemma picks 8:00 a.m. in Costa del Este, and the raincoat goes on the list from the morning because of the afternoon.
Here is the same route when I write, in my own words, that I want to walk during lunch:
Gemma respects the time I asked for and warns me about the rain and heat. The red Ojo ("heads up") line is not written by the model. The code adds it whenever the chosen window scores poorly. I'll explain why below.
Watch the 31-second demo video. It has no narration and shows the command and the resulting card for three scenes. The interface is in Spanish, because the people I built it for speak Spanish.
En octubre llueve en Ciudad de Panamá casi todas las tardes. Ventana seca mira el pronóstico por hora de los próximos días, calcula los tramos de luz con menos lluvia, calor y sol fuerte para salir a caminar o correr, y deja que Gemma, un modelo de pesos abiertos que corre en tu propio equipo con Ollama, escoja uno según lo que tú le pidas. El resultado es una tarjeta de un vistazo y un recordatorio en el calendario que avisa media hora antes, para que guardes el teléfono y salgas.
In English. Ventana seca ("dry window") finds the best rain-free window to go outside in Panama City during the rainy season. It scores hourly forecasts from Open-Meteo, lets Google's open-weight Gemma model, running locally through Ollama, pick one of those windows based on what you ask for in plain words, and writes a one-glance card plus…
MIT license, Python standard library only (plus pytest for the 25 tests). Setup:
git clone https://github.com/yosef7/ventana-seca.git
cd ventana-seca
uv sync
ollama pull gemma4:e2b
uv run python -m ventana_seca --ruta "7:30 5 de mayo, 8:00 costa del este, 12 pm costa del este, 4 pm costa del este"
The route is saved, so the next mornings it's just uv run python -m ventana_seca --mi-ruta.
The split: code calculates, the model interprets. Numbers are where a small model is most likely to slip, so the code handles all of them:
ventana field only accepts the labels of the computed windows, and whose llevar (things to carry) field only accepts items from a closed list.
What testing with the real forecast taught me. The first version worked, and then the real runs showed me five problems. Each one changed the design:
| What I saw | What I changed |
|---|---|
| With Gemma 4's thinking mode on, a choice took 38 to 114 seconds | Picking among five options doesn't need long reasoning: think: false brought it down to7–9 seconds |
| Gemma called a 53% chance of rain "poca probabilidad" (low probability) | The code now adds the Ojo line for any window scoring under 60, whatever the model says |
| Gemma wrote "la opción B" in its explanation, a letter the card never shows | Gemma now chooses among readable labels like "mar 6 oct, 12:00 p. m. en Costa del Este" , so there's no internal code to leak |
| Asked for lunch, it picked 8:00 a.m. without saying why it rejected noon | New instruction: if the requested time exists, honor it and warn clearly |
| It offered 7:00–8:00 and 8:00–9:00 as different options | Windows on the same day must be three hours apart |
The pattern I ended up with: the model is good at understanding what I want, and the code is in charge of the facts and the safety warnings. Every number on the card comes from the forecast, never from the model.
The full list of decisions is in docs/arquitectura.md, and every test run is in docs/validacion.md.
On Tuesday, October 6, I followed my usual route with the card from the demo above. Here is the forecast next to what actually happened:
| Stop | Forecast | What happened |
|---|---|---|
| 7:30 a.m. · Plaza 5 de Mayo | 20% · 31 °C · dry | Dry |
| 8:00 a.m. · Costa del Este | 18% · 31 °C · dry | Dry under a gray sky |
| 12:00 p.m. · Costa del Este | 66% · 39 °C · rain and heat | It rained |
| 4:00 p.m. · Costa del Este | 88% · 31 °C · rain | It rained |
The day went the way the card said it would. The stop Gemma picked, 8:00 a.m. in Costa del Este, was dry. I took this photo at 8:15:
Then noon arrived as forecast. At 12:43 p.m., through a window in Costa del Este, the rain was streaking the glass and the hills in the distance had disappeared:
The raincoat, which the card put on the list in the morning because of the afternoon, was needed twice. At 5:52 p.m. the pavement was still wet:
One honest detail: when I checked Open-Meteo's hourly record for the same coordinates that night, it showed the storm between 11 a.m. and 1 p.m. (8.3 mm at noon) and 0 mm at 4 p.m., even though I saw rain in the afternoon. The record is a model estimate for a grid cell several kilometers wide, and a local afternoon shower can fall outside it. The full comparison is in docs/validacion.md.
What I write about my day stays on my laptop. The preference text can be personal: when you leave work, who you're going out with. The saved route is literally my daily location pattern. With Gemma running locally, none of that goes to a server. The only request that leaves the computer is the forecast, and it carries the coordinates of a public place, not my GPS, with no account and no API key.
It costs nothing to run every morning. No per-request fee, no quota, no subscription. That matters for a tool you're supposed to use daily.
I could see and change how the model behaves. Because the model and the runtime are open, I measured the thinking mode, turned it off, and constrained the output with a schema. When the model softened a 53% rain chance, I could build a guardrail around its exact behavior instead of hoping a hosted API wouldn't change under me. The model is also swappable with --modelo, for example gemma4:e4b on a machine with more memory.
It works where the signal doesn't. Gemma doesn't need the internet, and the forecast is cached. On a trail with no coverage, the card still comes from the last forecast and says when it was saved.
To be honest about the trade-off: a large hosted model would probably write nicer explanations. But this job needed control and privacy more than eloquence, and the small model's mistakes are exactly the kind the code can catch.
AI assistance disclosure: I built Ventana seca with Claude Code (Claude Opus 5.5) as my coding agent: it implemented the code, tests and documentation, generated the demo, and drafted this post from my route and the recorded test results. I picked the idea, supplied my real route, reviewed the results and tested it on my own commute.