<?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: Arnulfo</title>
    <description>The latest articles on DEV Community by Arnulfo (@arnulfo_07).</description>
    <link>https://dev.to/arnulfo_07</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%2F96018%2Fcc30ba29-dddc-4a48-90d7-8cee700edb2f.JPG</url>
      <title>DEV Community: Arnulfo</title>
      <link>https://dev.to/arnulfo_07</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arnulfo_07"/>
    <language>en</language>
    <item>
      <title>Ventana seca: finding the dry hour to get outside in Panama's rainy season, with Gemma on my laptop</title>
      <dc:creator>Arnulfo</dc:creator>
      <pubDate>Wed, 07 Oct 2026 02:08:46 +0000</pubDate>
      <link>https://dev.to/arnulfo_07/ventana-seca-finding-the-dry-hour-to-get-outside-in-panamas-rainy-season-with-gemma-on-my-laptop-44fd</link>
      <guid>https://dev.to/arnulfo_07/ventana-seca-finding-the-dry-hour-to-get-outside-in-panamas-rainy-season-with-gemma-on-my-laptop-44fd</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;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ventana seca&lt;/strong&gt; ("dry window") answers one question: &lt;em&gt;when today can I be outside without getting soaked or cooked?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;This is my real route for Tuesday, October 6, with the real forecast and Gemma choosing on my laptop:&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%2F4wxw9x88kl3t1027zeq9.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%2F4wxw9x88kl3t1027zeq9.png" alt="Ventana seca route card: Plaza 5 de Mayo at 7:30 a.m., Costa del Este at 8:00 a.m., 12:00 p.m. and 4:00 p.m.; Gemma picks 8:00 a.m." width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Here is the same route when I write, in my own words, that I want to walk during lunch:&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%2Fmb6dtcyc9oyu2c61z2h4.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%2Fmb6dtcyc9oyu2c61z2h4.png" alt="Same route with a lunch preference: Gemma picks 12:00 p.m. and warns about rain and heat; the code adds an " width="799" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gemma respects the time I asked for and warns me about the rain and heat. The red &lt;strong&gt;Ojo&lt;/strong&gt; ("heads up") line is not written by the model. The code adds it whenever the chosen window scores poorly. I'll explain why below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/yosef7/ventana-seca/blob/main/demo/ventana-seca-demo.mp4" rel="noopener noreferrer"&gt;Watch the 31-second demo video&lt;/a&gt;. 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.&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/yosef7" rel="noopener noreferrer"&gt;
        yosef7
      &lt;/a&gt; / &lt;a href="https://github.com/yosef7/ventana-seca" rel="noopener noreferrer"&gt;
        ventana-seca
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Find the rain-free window to go outside in Panama City, chosen by Gemma running locally. DEV Hacktoberfest 2026 · Week 1: Touch Grass
    &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;Ventana seca&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;En octubre llueve en Ciudad de Panamá casi todas las tardes. &lt;strong&gt;Ventana seca&lt;/strong&gt; 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 &lt;strong&gt;Gemma&lt;/strong&gt;, 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.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In English.&lt;/strong&gt; &lt;em&gt;Ventana seca&lt;/em&gt; ("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 &lt;strong&gt;Gemma&lt;/strong&gt; 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…&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/yosef7/ventana-seca" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;MIT license, Python standard library only (plus &lt;code&gt;pytest&lt;/code&gt; for the 25 tests). Setup:&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/yosef7/ventana-seca.git
&lt;span class="nb"&gt;cd &lt;/span&gt;ventana-seca
uv &lt;span class="nb"&gt;sync
&lt;/span&gt;ollama pull gemma4:e2b
uv run python &lt;span class="nt"&gt;-m&lt;/span&gt; ventana_seca &lt;span class="nt"&gt;--ruta&lt;/span&gt; &lt;span class="s2"&gt;"7:30 5 de mayo, 8:00 costa del este, 12 pm costa del este, 4 pm costa del este"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The route is saved, so the next mornings it's just &lt;code&gt;uv run python -m ventana_seca --mi-ruta&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The split: code calculates, the model interprets.&lt;/strong&gt; Numbers are where a small model is most likely to slip, so the code handles all of them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Forecast.&lt;/strong&gt; Ventana seca asks &lt;a href="https://open-meteo.com/" rel="noopener noreferrer"&gt;Open-Meteo&lt;/a&gt; for hourly rain probability, millimeters, heat index, UV and daylight. It asks for the coordinates of the &lt;em&gt;place&lt;/em&gt; (a park, a plaza), never mine, and keeps a local copy so it still works without signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoring, no AI.&lt;/strong&gt; Each window starts at 100 points and loses points for rain probability, expected millimeters, heat index above 32 °C and UV above 7. The code keeps up to five daylight windows, at most two per day and at least three hours apart, so there are real alternatives (morning vs. late afternoon). In route mode, each stop of my day is a candidate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choice, with Gemma.&lt;/strong&gt; &lt;a href="https://ai.google.dev/gemma" rel="noopener noreferrer"&gt;Gemma 4 E2B&lt;/a&gt; runs locally through &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; (the 4.6 GB Q4_K_M build, on a MacBook with 8 GB of RAM). It receives the candidates with their forecast and whatever I wrote: &lt;em&gt;"quiero caminar en la hora del almuerzo"&lt;/em&gt;, &lt;em&gt;"voy con mi hija de 6 años, que no aguanta el calor"&lt;/em&gt;. Its reply is constrained with Ollama's structured output: a JSON Schema whose &lt;code&gt;ventana&lt;/code&gt; field only accepts the labels of the computed windows, and whose &lt;code&gt;llevar&lt;/code&gt; (things to carry) field only accepts items from a closed list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation and fallback.&lt;/strong&gt; If Ollama isn't running, or the answer is invalid, a fixed rule picks the highest score. The tool always answers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-negotiables in code.&lt;/strong&gt; Water always; raincoat at 30% rain or more; cap and sunscreen at UV 6+; repellent on forest trails; a flashlight if the window touches darkness. In route mode they're computed for the whole day.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What testing with the real forecast taught me.&lt;/strong&gt; The first version worked, and then the real runs showed me five problems. Each one changed the design:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What I saw&lt;/th&gt;
&lt;th&gt;What I changed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;With Gemma 4's thinking mode on, a choice took &lt;strong&gt;38 to 114 seconds&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Picking among five options doesn't need long reasoning: &lt;code&gt;think: false&lt;/code&gt; brought it down to &lt;strong&gt;7–9 seconds&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma called a &lt;strong&gt;53% chance of rain&lt;/strong&gt; "poca probabilidad" (low probability)&lt;/td&gt;
&lt;td&gt;The code now adds the &lt;strong&gt;Ojo&lt;/strong&gt; line for any window scoring under 60, whatever the model says&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma wrote "la opción B" in its explanation, a letter the card never shows&lt;/td&gt;
&lt;td&gt;Gemma now chooses among readable labels like &lt;em&gt;"mar 6 oct, 12:00 p. m. en Costa del Este"&lt;/em&gt;, so there's no internal code to leak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asked for lunch, it picked 8:00 a.m. without saying why it rejected noon&lt;/td&gt;
&lt;td&gt;New instruction: if the requested time exists, honor it and warn clearly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;It offered 7:00–8:00 and 8:00–9:00 as different options&lt;/td&gt;
&lt;td&gt;Windows on the same day must be three hours apart&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern I ended up with: &lt;strong&gt;the model is good at understanding what I want, and the code is in charge of the facts and the safety warnings.&lt;/strong&gt; Every number on the card comes from the forecast, never from the model.&lt;/p&gt;

&lt;p&gt;The full list of decisions is in &lt;a href="https://github.com/yosef7/ventana-seca/blob/main/docs/arquitectura.md" rel="noopener noreferrer"&gt;docs/arquitectura.md&lt;/a&gt;, and every test run is in &lt;a href="https://github.com/yosef7/ventana-seca/blob/main/docs/validacion.md" rel="noopener noreferrer"&gt;docs/validacion.md&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking It Outside
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stop&lt;/th&gt;
&lt;th&gt;Forecast&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;7:30 a.m. · Plaza 5 de Mayo&lt;/td&gt;
&lt;td&gt;20% · 31 °C · dry&lt;/td&gt;
&lt;td&gt;Dry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8:00 a.m. · Costa del Este&lt;/td&gt;
&lt;td&gt;18% · 31 °C · dry&lt;/td&gt;
&lt;td&gt;Dry under a gray sky&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12:00 p.m. · Costa del Este&lt;/td&gt;
&lt;td&gt;66% · 39 °C · rain and heat&lt;/td&gt;
&lt;td&gt;It rained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4:00 p.m. · Costa del Este&lt;/td&gt;
&lt;td&gt;88% · 31 °C · rain&lt;/td&gt;
&lt;td&gt;It rained&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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:&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%2Fev0py4zpaxhcdz961pf3.jpg" 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%2Fev0py4zpaxhcdz961pf3.jpg" alt="Costa del Este at 8:15 a.m. on October 6: overcast sky, palm trees and dry pavement" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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:&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%2F53wbzuo53kea3dqbm2hx.jpg" 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%2F53wbzuo53kea3dqbm2hx.jpg" alt="Costa del Este at 12:43 p.m. on October 6, seen through a window: rain on the glass and a hazy horizon" width="800" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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:&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%2F51jvu165cepbxqyvs3le.jpg" 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%2F51jvu165cepbxqyvs3le.jpg" alt="Costa del Este at 5:52 p.m. on October 6: wet pavement, traffic and a heavy gray sky" width="800" height="1422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://github.com/yosef7/ventana-seca/blob/main/docs/validacion.md#3-prueba-afuera-ruta-del-martes-6-oct-2026" rel="noopener noreferrer"&gt;docs/validacion.md&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What I write about my day stays on my laptop.&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It costs nothing to run every morning.&lt;/strong&gt; No per-request fee, no quota, no subscription. That matters for a tool you're supposed to use daily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I could see and change how the model behaves.&lt;/strong&gt; 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 &lt;code&gt;--modelo&lt;/code&gt;, for example &lt;code&gt;gemma4:e4b&lt;/code&gt; on a machine with more memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It works where the signal doesn't.&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&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 E2B, running locally with Ollama, is the decision-maker at the core of Ventana seca. It interprets a free-text preference, chooses among computed windows through a constrained JSON Schema, and writes the explanation on the card. Its behavior shaped the design: thinking mode off for speed, readable labels instead of letters, and a code-side warning when it understates risk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI assistance disclosure:&lt;/strong&gt; 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.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>hacktoberfest</category>
      <category>ai</category>
    </item>
    <item>
      <title>Sendero: a local catechesis record with open AI for a chapel community in San Miguelito, Panama</title>
      <dc:creator>Arnulfo</dc:creator>
      <pubDate>Sun, 04 Oct 2026 14:51:26 +0000</pubDate>
      <link>https://dev.to/arnulfo_07/sendero-helping-noris-prepare-each-childs-next-step-with-local-open-ai-4gl3</link>
      <guid>https://dev.to/arnulfo_07/sendero-helping-noris-prepare-each-childs-next-step-with-local-open-ai-4gl3</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;Sendero&lt;/strong&gt; is a catechesis record built for the community of the &lt;strong&gt;Capilla Nuestra Señora de Lourdes&lt;/strong&gt; in Valle de Urraca, San Miguelito, Panama. The chapel's catechists accompany children through several stages of formation, and they need one reliable place to record each child's journey: enrollment, attendance, the formation received, the requirements met, and each change of stage.&lt;/p&gt;

&lt;p&gt;Sendero turns that record into preparation for the next meeting. Its Spanish interface brings together periods, groups, participant profiles, multiple responsible adults, attendance, observations, stage requirements and a chronological history.&lt;/p&gt;

&lt;p&gt;The open AI feature is at the start of each child's page: &lt;strong&gt;Preparar acompañamiento&lt;/strong&gt;. An open-weight model selects and prioritizes two or three reviewed preparation actions and a question for the child's responsible adult, using the current requirement states and attendance counts. The proposal is saved with its model and evidence snapshot. If the record changes, Sendero marks it outdated.&lt;/p&gt;

&lt;p&gt;The catechesis team can create a dated period and its groups, enroll a participant with multiple responsible adults, record attendance and class topics under that enrollment, and consult the complete history. Existing profiles survive the schema upgrade; new enrollments do not duplicate the participant. A catechist can mark a current AI proposal as reviewed, which records the review without changing requirements or stages.&lt;/p&gt;

&lt;p&gt;The goal is deliberately modest: help the team arrive at the next meeting knowing what to review, while every decision about a child's progress remains with the catechists.&lt;/p&gt;

&lt;p&gt;Sendero is intended to be handed over to the chapel community. So far I have tested the working prototype with fictional records only. Reviewing the actual stage names and requirements with the catechesis team is the next acceptance step; I do not yet have their feedback to report.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/yosef7/sendero-catequesis/blob/main/demo/sendero-demo.mp4" rel="noopener noreferrer"&gt;Watch the 49-second demonstration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The recording shows the complete workflow: login with a fictional demo code, period and group creation, a participant with two responsible adults, attendance and a class topic, an observation, a real local model proposal, human review, and persistence after reload. All displayed records are fictional. The application is in Spanish; the video has no narration, and the wait while the local model generates its proposal is shown in real time.&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%2Fnlufdgyoyfqhkoqthdl9.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%2Fnlufdgyoyfqhkoqthdl9.png" alt="Sendero periods and groups" width="800" height="500"&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%2Fkgfy2l1dmv0utefck0oh.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%2Fkgfy2l1dmv0utefck0oh.png" alt="Sendero's local AI preparation proposal, with attendance and requirement counts" width="800" height="1304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/yosef7/sendero-catequesis" rel="noopener noreferrer"&gt;Source code, setup instructions and MIT license&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The model has its own license: the &lt;a href="https://huggingface.co/Qwen/Qwen2.5-Coder-3B-Instruct" rel="noopener noreferrer"&gt;Qwen2.5-Coder-3B-Instruct model card&lt;/a&gt; identifies it as &lt;code&gt;qwen-research&lt;/code&gt;. The application's MIT license does not change those model terms.&lt;/p&gt;

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

&lt;p&gt;The application uses Python, Flask, SQLite, plain JavaScript and Ollama. I chose the available local &lt;code&gt;qwen2.5-coder:3b&lt;/code&gt; model for the prototype and kept the adapter configurable through &lt;code&gt;OLLAMA_MODEL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The modules separate the interface, HTTP API, formation rules, persistence and local inference. SQLite foreign keys and transactions keep the records consistent; stage advancement also checks the expected current stage to reject a repeated transition. Versioned migrations add saved AI proposals, periods, groups, enrollments, responsible adults and human review without replacing existing child records. An invalid initial enrollment rolls back the entire new profile. Attendance must belong to the participant and fall within the enrollment period.&lt;/p&gt;

&lt;p&gt;The model receives a small context: numbered requirements and their completion states, recorded-class and attendance counts, the current stage identifier and whether another stage exists. Names, contacts, class topics and free-text observations stay out of the model request.&lt;/p&gt;

&lt;p&gt;My first free-text generation was structurally valid but suggested details not supported by the record. I changed the design: the JSON Schema supplies an allowed catalog of actions and questions, and the server validates membership and rejects duplicate actions before saving. The model can choose and prioritize within that catalog; it cannot introduce a new requirement through its response.&lt;/p&gt;

&lt;p&gt;That is a deliberate limit on generative freedom. Sendero never treats AI output as proof that a requirement is complete, and the model has no tools to write to the formation record.&lt;/p&gt;

&lt;p&gt;The expanded workflow passes &lt;strong&gt;26 automated tests&lt;/strong&gt;. The final Chromium run completed login, period and group creation, enrollment with two responsible adults, attendance and topic entry, local inference, review, reload and a mobile contact edit with zero JavaScript errors. Four views at both 390 and 320 pixels had no horizontal overflow. Those are emulated mobile checks, not a test on a physical phone.&lt;/p&gt;

&lt;p&gt;In the recorded run, the real Ollama request returned a valid proposal in &lt;strong&gt;17.79 seconds&lt;/strong&gt;, with the model not yet loaded in memory; an earlier run with the model already loaded took 3.34 seconds. These observations describe this machine and those requests, not a performance guarantee. The &lt;a href="https://github.com/yosef7/sendero-catequesis/blob/main/demo/validacion-v1.json" rel="noopener noreferrer"&gt;browser validation&lt;/a&gt; records the final checks. The previous &lt;a href="https://github.com/yosef7/sendero-catequesis/blob/main/demo/evaluacion-ia.json" rel="noopener noreferrer"&gt;three-case evaluation&lt;/a&gt; remains available as evidence from the earlier prototype.&lt;/p&gt;

&lt;p&gt;The implementation follows &lt;a href="https://docs.ollama.com/capabilities/structured-outputs" rel="noopener noreferrer"&gt;Ollama’s structured-output contract&lt;/a&gt;. The earlier structural-validation approach was informed by &lt;a href="https://dev.to/devshakib/structured-output-from-llms-a-retry-repair-loop-your-parser-never-sees-through-3b0b"&gt;DevShakib's article on structured output&lt;/a&gt; and &lt;a href="https://dev.to/mukundakatta/rule-based-llm-output-validation-reject-bad-responses-before-they-reach-your-users-if0"&gt;Mukunda Katta's explanation of rule-based output validation&lt;/a&gt;. A valid response is still a proposal for the catechists to review, not a guarantee that its priorities are useful.&lt;/p&gt;

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

&lt;p&gt;This project concerns children, so local operation is a practical design choice. Once the dependencies and model are downloaded, recording and inference can run without an external inference service, a subscription or a paid API key. A parish community can keep its own records on its own computer, and the AI request carries only numerical progress information that never leaves that machine.&lt;/p&gt;

&lt;p&gt;Open local inference also let me inspect the request, change the response contract after an unsatisfactory result, and repeat the experiment. The community can try a different local model through the same adapter, respecting its license, without rewriting the registration and formation modules. I have not fine-tuned the model or claimed that this is impossible with every closed API.&lt;/p&gt;

&lt;p&gt;The current version runs on one trusted computer with a single shared access code. It includes local access protection, a downloadable SQLite backup and printable records. Individual accounts for each catechist, retention and consent procedures, testing on a physical phone and acceptance testing with the catechesis team remain future work. The demonstration uses fictional data throughout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI assistance disclosure:&lt;/strong&gt; I used Codex to help implement, test and document Sendero, and to draft this article from the stated need and recorded validation results. I used Claude Code to revise the article and documentation for the community-focused version and to re-record the demonstration.&lt;/p&gt;

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