<?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: Eidan Rosado</title>
    <description>The latest articles on DEV Community by Eidan Rosado (@edyvision).</description>
    <link>https://dev.to/edyvision</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%2F530164%2Fe73fb8ea-4071-4967-b517-f798cab16e4c.jpg</url>
      <title>DEV Community: Eidan Rosado</title>
      <link>https://dev.to/edyvision</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/edyvision"/>
    <language>en</language>
    <item>
      <title>Linterna: keep the voice behind the memory</title>
      <dc:creator>Eidan Rosado</dc:creator>
      <pubDate>Tue, 27 Oct 2026 22:06:12 +0000</pubDate>
      <link>https://dev.to/edyvision/linterna-keep-the-voice-behind-the-memory-3kk8</link>
      <guid>https://dev.to/edyvision/linterna-keep-the-voice-behind-the-memory-3kk8</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;A transcript can tell you what someone said. It cannot keep the little pause before a punchline, the way they pronounce your name, or the laugh halfway through a story.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;Linterna&lt;/strong&gt;, a small, local oral-history app for keeping those things together: the recording, the words, and a way to find the memory again.&lt;/p&gt;

&lt;p&gt;For this week's theme, I leaned toward remembrance. A lantern is something you light to keep a person or a story present. In Linterna, you tap a watercolor lantern, tell a memory, and keep it. Smaller lanterns float independently through the night sky, with an occasional shooting star crossing above them.&lt;/p&gt;

&lt;p&gt;The interaction is deliberately small. There is no account to create and no conversation feed to manage. Pick a story by date, search its words, and listen.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fauat9cbck00e3uhs91rm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fauat9cbck00e3uhs91rm.gif" alt="Linterna records a memory, transcribes it, saves it and plays the original recording" width="720" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The GIF uses synthetic speech to demonstrate the actual browser recording, transcription, save and playback flow. No family recording or private memory is included. The transcription runs against my existing private speech service.&lt;/p&gt;

&lt;p&gt;You can tap to start and tap again to finish, or press and hold. Once the recording ends, you can preview your voice and edit the transcript before saving. If transcription is unavailable, the recording can still be saved. Your story does not disappear because a model request failed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Play recording&lt;/strong&gt; plays the original voice. &lt;strong&gt;Read transcript&lt;/strong&gt; is a separate narration option. That distinction matters: a generated voice should never quietly replace the person who told the story.&lt;/p&gt;

&lt;p&gt;Linterna runs locally. There is no public instance collecting visitors' family recordings.&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/EdyVision" rel="noopener noreferrer"&gt;
        EdyVision
      &lt;/a&gt; / &lt;a href="https://github.com/EdyVision/linterna" rel="noopener noreferrer"&gt;
        linterna
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &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;Linterna&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Keep a voice, not just a transcript. Linterna is a local oral-history lantern
record a memory, keep the original audio, find its words later and hear it again
No accounts. Memories and recordings stay together in the local SQLite database.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/EdyVision/linterna/docs/linterna-demo.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2FEdyVision%2Flinterna%2FHEAD%2Fdocs%2Flinterna-demo.gif" alt="Record, transcribe, save and replay a memory"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The demo uses synthetic speech, with real private speech inference and local storage.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Requirements&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.11 or newer and &lt;code&gt;make&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A current Chrome, Edge or Safari browser with microphone access.&lt;/li&gt;
&lt;li&gt;FFmpeg is optional, recommended for seekable browser recordings. On macOS,
&lt;code&gt;brew install ffmpeg&lt;/code&gt;; on Debian/Ubuntu, &lt;code&gt;sudo apt install ffmpeg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Node.js 22+ is needed only for the recorder tests, not to run the app.&lt;/li&gt;
&lt;li&gt;A compatible speech endpoint is optional. Recording, local saving, manual
transcripts and original-voice playback work without one.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/EdyVision/linterna.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; linterna
make install
make start&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open &lt;a href="http://127.0.0.1:8787" rel="nofollow noopener noreferrer"&gt;http://127.0.0.1:8787&lt;/a&gt;. Allow the microphone when you choose to record
Keep that terminal running; Ctrl+C…&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/EdyVision/linterna" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/EdyVision/linterna" rel="noopener noreferrer"&gt;Source code and setup instructions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The application code is MIT licensed. The browser interface is plain JavaScript, HTML and CSS, with a FastAPI server and SQLite storage.&lt;/p&gt;

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

&lt;p&gt;The speech models do practical work here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NVIDIA Nemotron ASR&lt;/strong&gt; transcribes the recording through my privately hosted, compatible speech endpoint. The tested deployment uses the &lt;code&gt;nemotron-3.5-asr-streaming-0.6b.q8_0&lt;/code&gt; model. NVIDIA documents the underlying &lt;a href="https://build.nvidia.com/nvidia/nemotron-asr-streaming/modelcard" rel="noopener noreferrer"&gt;Nemotron ASR family here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Magpie TTS&lt;/strong&gt; provides optional narration for a written transcript. It is useful for text-only memories, but the saved original recording remains the primary playback option. &lt;a href="https://docs.nvidia.com/nim/speech/26.05.0/tts/" rel="noopener noreferrer"&gt;NVIDIA's speech documentation&lt;/a&gt; describes the TTS service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although the ASR model supports streaming, this version transcribes a completed recording. I am not claiming live captions or a streaming speech interface.&lt;/p&gt;

&lt;p&gt;The browser uses &lt;code&gt;getUserMedia&lt;/code&gt; and &lt;code&gt;MediaRecorder&lt;/code&gt; to capture real audio. A separate WAV conversion sends mono, 16 kHz audio to ASR, while the original recording is kept for playback. The service URL and model ID are configurable; my infrastructure is not required to run someone else's copy.&lt;/p&gt;

&lt;p&gt;SQLite stores the audio and transcript in the same memory record. That keeps saving and deletion together, without loose recording files becoming separated from their stories. SQLite FTS and a small TF-IDF ranking pass support topic searches. The latter is local text ranking, not an embeddings model or an LLM pretending to remember your family.&lt;/p&gt;

&lt;p&gt;A few less visible details made a real difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recording data can arrive after Stop is pressed. I wait for the final recorder event before offering the audio to save.&lt;/li&gt;
&lt;li&gt;A late microphone permission result cannot restart a cancelled recording.&lt;/li&gt;
&lt;li&gt;Failed transcription keeps the draft audio available.&lt;/li&gt;
&lt;li&gt;Existing text-only memories survive the schema migration.&lt;/li&gt;
&lt;li&gt;Browser recordings can lack useful duration metadata. When FFmpeg is installed, the server remuxes the container without re-encoding the voice, so playback is seekable.&lt;/li&gt;
&lt;li&gt;Configured speech credentials stay on the server and are bound to their configured origin. They are not returned to the browser or committed to the repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Validation includes ten backend tests and five recorder/WAV tests, plus a real browser run through capture, private transcription, saving, page refresh and original-audio playback. The GIF comes from that working flow.&lt;/p&gt;

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

&lt;p&gt;An oral-history tool should help you keep something, not create another dependency you are afraid of losing.&lt;/p&gt;

&lt;p&gt;Open-weight speech models let me run transcription and narration on infrastructure I control. I do not need a new transcription subscription to capture each story, and the app does not need a particular vendor's account system to work.&lt;/p&gt;

&lt;p&gt;The recordings and transcripts live in an ordinary local SQLite database. The code is available, the speech endpoint can be changed, and the original voice is retained independently of the model output.&lt;/p&gt;

&lt;p&gt;That does not make every configuration automatically private or offline. My tested setup uses private network speech services. Point Linterna at an external provider and that provider receives the audio or text you send. Browser speech captions can also depend on the browser vendor. Keeping that boundary clear is part of building something trustworthy.&lt;/p&gt;

&lt;p&gt;I also kept the transcript editable. Speech recognition can get names and words wrong, including this demo's spelling of “Linterna.” The original recording remains there to check against. A model's best guess should not become the only surviving version of a memory.&lt;/p&gt;

&lt;p&gt;I used Codex to help develop and validate Linterna and prepare this write-up. The recording, transcription, storage and playback described here were exercised, rather than represented by a mock transcript.&lt;/p&gt;

&lt;p&gt;My aim for this final week was simple: make something small enough to finish and useful enough to keep. A voice, a story, and a lantern you can return to.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>I taught my release checklist to wait for me</title>
      <dc:creator>Eidan Rosado</dc:creator>
      <pubDate>Fri, 23 Oct 2026 04:01:48 +0000</pubDate>
      <link>https://dev.to/edyvision/i-taught-my-release-checklist-to-wait-for-me-5bm0</link>
      <guid>https://dev.to/edyvision/i-taught-my-release-checklist-to-wait-for-me-5bm0</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;I shipped a CLI release. Homebrew was still installing an older version.&lt;/p&gt;

&lt;p&gt;That is a particularly annoying kind of failure. The release exists, the build has finished, and you are ready to tell people to try it. Then you discover that “published” and “installable” are two different things.&lt;/p&gt;

&lt;p&gt;For a small open-source project, the release checklist tends to live in the maintainer's head. Check the tag. Check the assets. Check the package version. Check the links. Remember what failed. Come back later and remember why.&lt;/p&gt;

&lt;p&gt;I wanted to stop carrying that context around.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Miilo Release Caretaker is a release-review workflow that gathers evidence, explains what needs attention, and waits for a person to return.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mastra runs the workflow and preserves its state. Gemma investigates the evidence. Miilo can display the resulting work and attention through an optional local integration.&lt;/p&gt;

&lt;p&gt;The important boundary: &lt;strong&gt;the current release checks use labeled sample facts.&lt;/strong&gt; The Mastra execution, SQLite persistence, live Gemma inference, evidence tools and local native CLI bridge are real. Automatic GitHub, Homebrew and documentation-site inspection are the next adapters, not a capability I am claiming today.&lt;/p&gt;

&lt;p&gt;This week's project establishes the part I need to trust before attaching it to real releases: can the workflow pause, survive interruption, explain its findings and preserve the evidence when I return?&lt;/p&gt;

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

&lt;p&gt;No voiceover required. This captioned GIF condenses three excerpts from the same verified Linux recording:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzrqulamji0s43353yobe.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzrqulamji0s43353yobe.gif" alt="Gemma reads sample release evidence, Mastra reopens the saved review, and fresh verification clears attention while preserving failed evidence" width="760" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The journey starts with a sample package at &lt;strong&gt;1.1.0&lt;/strong&gt; when the release expects &lt;strong&gt;1.2.0&lt;/strong&gt;. Gemma reads the evidence, explains the mismatch and the workflow waits for review. A separate process then reopens the saved run and exports its observation to Miilo.&lt;/p&gt;

&lt;p&gt;Finally, an explicit recheck switches the sample source to corrected facts. Current attention clears; the original failed evidence remains. The command does not edit a real formula or publish a release.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/miilo-ai/miilo-release-caretaker" rel="noopener noreferrer"&gt;&lt;strong&gt;Miilo Release Caretaker on GitHub&lt;/strong&gt;&lt;/a&gt; · Apache-2.0&lt;/p&gt;

&lt;p&gt;The base workflow runs independently of an installed Miilo CLI. With Node.js 22.13 or later:&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/miilo-ai/miilo-release-caretaker.git
&lt;span class="nb"&gt;cd &lt;/span&gt;miilo-release-caretaker
npm ci &lt;span class="nt"&gt;--ignore-scripts&lt;/span&gt;
npm run check
npm &lt;span class="nb"&gt;test
&lt;/span&gt;npm run journey &lt;span class="nt"&gt;--&lt;/span&gt; start stale-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last command creates a persisted sample review. Live model investigation is explicit opt-in through an owner-only configuration file; ordinary tests never call my model endpoint or yours.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/miilo-ai/miilo-release-caretaker/blob/main/docs/model-investigation.md" rel="noopener noreferrer"&gt;model configuration guide&lt;/a&gt; explains endpoint, model and credential-file configuration. The &lt;a href="https://github.com/miilo-ai/miilo-release-caretaker/tree/main/integration/miilo" rel="noopener noreferrer"&gt;optional Miilo bridge&lt;/a&gt; requires the matching local SDK/CLI changes. Those changes are not yet a published Miilo feature.&lt;/p&gt;

&lt;p&gt;Release Caretaker is the new Week 3 project. The broader Miilo observer project predates this challenge.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Mastra gives the work a lifecycle
&lt;/h3&gt;

&lt;p&gt;A checklist script can report a mismatch. I needed the review itself to survive after the command exits.&lt;/p&gt;

&lt;p&gt;The workflow runs three parallel check groups: release, package and links. They join into one report, pass through investigation, and reach a human-review step. This is the actual composition from the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parallel&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nf"&gt;checkStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;release&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;checkStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;package&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;checkStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;links&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;investigationStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;investigator&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reviewStep&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;verificationStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mastra supplies execution, agent tools and suspension/resumption, with its LibSQL storage adapter backing saved workflow snapshots. The application adds report revisions, deterministic verdicts and the rules for what a review means.&lt;/p&gt;

&lt;p&gt;A failed check remains evidence with expected and actual values. A source throwing an error exercises bounded retries. These are different events: finishing the workflow does not make its release checks pass.&lt;/p&gt;

&lt;p&gt;Review is tied to a SHA-256 revision of the report. If the submitted revision does not match, the workflow suspends again. Acknowledging a finding preserves its failed verdict. Requesting verification reads the source again and produces a separate verification report.&lt;/p&gt;

&lt;p&gt;That distinction matters when I return after a break. My review should describe the evidence I actually saw, and “I looked at it” should never mean “the problem is fixed.”&lt;/p&gt;

&lt;h3&gt;
  
  
  Gemma makes the evidence easier to act on
&lt;/h3&gt;

&lt;p&gt;The version comparison does not need an LLM. Explaining a collection of findings and proposing a useful next step is where I wanted model assistance.&lt;/p&gt;

&lt;p&gt;I used &lt;strong&gt;Gemma 3 12B&lt;/strong&gt;, served on hardware I already own through a configurable OpenAI-compatible endpoint. A Mastra Agent receives the report's evidence identifiers and can use only two tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;list_evidence&lt;/code&gt;: list findings in the current report.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read_evidence&lt;/code&gt;: inspect one finding by its identifier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It cannot run shell commands, install a package, browse arbitrary URLs or change a repository. The final explanation must cite evidence the agent actually read through a tool. Knowing an identifier exists is insufficient.&lt;/p&gt;

&lt;p&gt;The endpoint used for the recording supports text chat rather than native function calls. An explicit JSON tool protocol translates Gemma's request into a tool call that Mastra executes. The model does not execute the tool itself. A separate native-tool mode is covered by disposable-server tests.&lt;/p&gt;

&lt;p&gt;In the recorded Omarchy run, Gemma inspected the formula evidence and explained the &lt;strong&gt;1.2.0 versus 1.1.0&lt;/strong&gt; mismatch using &lt;strong&gt;two model requests and one evidence-tool invocation in 11.3 seconds&lt;/strong&gt;. That is one observed case, not an accuracy benchmark. The endpoint did not report token usage, so token counts and inference cost remain unknown.&lt;/p&gt;

&lt;p&gt;Citation checks constrain the explanation; they do not prove the diagnosis is correct. Deterministic code owns the verdict, and a model error leaves that evidence available for human review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recovery needed more than a happy-path restart
&lt;/h3&gt;

&lt;p&gt;I tested reopening a saved review in another process. Then I killed a worker during an active parallel check.&lt;/p&gt;

&lt;p&gt;The second test exposed a different problem: a branch can finish reading before the parent parallel group commits its state. Restarting can repeat work that appeared complete.&lt;/p&gt;

&lt;p&gt;The adapter now checkpoints validated read results by run, scenario, check group and initial/verification stage. Recovery reuses committed results and repeats the unfinished read. A fresh verification cannot quietly reuse the initial stage's facts.&lt;/p&gt;

&lt;p&gt;This establishes a tested recovery boundary for the current read-only workflow. It does not establish exactly-once external writes or power-loss durability. A read interrupted before its checkpoint commits may repeat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Miilo gives the workflow somewhere to be seen
&lt;/h3&gt;

&lt;p&gt;Mastra remains the execution engine. Caretaker normalizes selected saved state into Miilo's workflow observation contract; the native CLI projects it into work items, attention and evidence.&lt;/p&gt;

&lt;p&gt;The practical payoff is that a review request can appear alongside other work rather than disappearing into a terminal scrollback. After verification, current attention can clear without deleting the failed evidence that explains the run's history.&lt;/p&gt;

&lt;p&gt;The bridge uses owner-only atomic file replacement and explicit enrollment. Tests cover interrupted exports, malformed files, stale sources and isolation between sources. The domain depends on typed ports; framework, storage, model and process details stay outside it.&lt;/p&gt;

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

&lt;p&gt;The two partner technologies doing the work here are &lt;strong&gt;Mastra and Gemma&lt;/strong&gt;. SQLite stores state, TypeScript implements the application, and Miilo supplies observation.&lt;/p&gt;

&lt;p&gt;Mastra lets me build on inspectable workflow and agent machinery. Gemma lets me run open-weight inference on existing hardware. Endpoint and model selection are configuration, so the workflow's domain does not require one model vendor or hosted subscription.&lt;/p&gt;

&lt;p&gt;This deployment uses a model-serving machine on a private network, so it is not an entirely offline laptop application. It avoids a new public inference service for the demo; existing hardware and electricity still have costs.&lt;/p&gt;

&lt;p&gt;More broadly, the checks, review rules and adapter interfaces are open to inspection. Someone can reproduce the sample journey, replace a check source or connect a compatible model without accepting my infrastructure as a dependency.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;I want to stop remembering the release checklist without surrendering the release decision.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The current prototype demonstrates that handoff: findings persist, Gemma explains the evidence, review can wait, and verification preserves history. The next step is to connect bounded read-only GitHub, formula and link adapters so it can review Miilo's actual releases.&lt;/p&gt;

&lt;p&gt;I have not measured hours saved or deployed automatic monitoring of real releases. What I have verified is concrete:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All 28 Caretaker tests passed on macOS, Omarchy and the repository's first Node 22 Linux CI run.&lt;/li&gt;
&lt;li&gt;Seven local SDK/CLI integration tests and 50 native CLI regression tests passed on Omarchy.&lt;/li&gt;
&lt;li&gt;Live Gemma investigation completed from both macOS and Omarchy.&lt;/li&gt;
&lt;li&gt;The recorded journey ended with zero current attention records while retaining historical failed evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository documents dependency-review and deployment limits. This is a local prototype, not a qualified public service.&lt;/p&gt;

&lt;p&gt;For this week's Slow and Steady theme, the useful part is giving the workflow permission to wait. I can leave the terminal without asking a model to make the final decision or relying on myself to reconstruct the whole story later.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Mastra:&lt;/strong&gt; parallel checks, bounded retries, durable review, cross-process resumption and a scoped evidence-tool agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; live, locally served Gemma inference for cited explanations of release evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;Two community discussions help explain the boundaries tested here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/hiroshi_takamura_c851fe71/tie-human-approval-to-the-exact-version-an-ai-agent-delivered-b6e"&gt;Tie Human Approval to the Exact Version an AI Agent Delivered&lt;/a&gt; by &lt;a href="https://dev.to/hiroshi_takamura_c851fe71"&gt;@hiroshi_takamura_c851fe71&lt;/a&gt;: why a review should identify the version and evidence it covers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aiexplore369zoho/function-calling-gives-llms-type-safety-not-correctness-5661"&gt;Function Calling Gives LLMs Type Safety, Not Correctness&lt;/a&gt; by &lt;a href="https://dev.to/aiexplore369zoho"&gt;@aiexplore369zoho&lt;/a&gt;: why validating structured output is not the same as validating its meaning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;AI assistance was used for implementation, testing and drafting. I review the project and submission before publication.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Miilo: Your AI, in sight</title>
      <dc:creator>Eidan Rosado</dc:creator>
      <pubDate>Sun, 18 Oct 2026 06:48:16 +0000</pubDate>
      <link>https://dev.to/edyvision/miilo-your-ai-in-sight-3d9k</link>
      <guid>https://dev.to/edyvision/miilo-your-ai-in-sight-3d9k</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I build software for clients, and AI agents have become part of that work. Keeping track of several sessions introduces a surprisingly ordinary problem: which one is working, which one is waiting, and which one needs me to make a decision?&lt;/p&gt;

&lt;p&gt;I wanted a useful answer at a glance. I also wanted it to be a little fun.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Miilo gives your AI workflows a tiny robot crew.&lt;/strong&gt; Each robot represents an observed system or group of sessions. Working robots move, resting robots close their eyes, and a &lt;code&gt;?!&lt;/code&gt; makes a request for attention hard to miss. Their colors follow your launcher theme.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Compact&lt;/strong&gt;, the crew fits into a small space. In &lt;strong&gt;Detail&lt;/strong&gt;, you can see the tasks behind each system. Select a task to inspect its notes, evidence and file changes. When you need help understanding it, &lt;strong&gt;Explain&lt;/strong&gt; brings a self-hosted Gemma model into the conversation.&lt;/p&gt;

&lt;p&gt;The immediate audience is freelancers, independent developers and small teams using several coding sessions or automation systems. They need to understand what happened before they approve the next step.&lt;/p&gt;

&lt;p&gt;The CLI, SDK, widgets and native Omarchy plugin are now public, with versioned GitHub releases and a Homebrew tap. My commercial hypothesis is that some of these users will pay for custom provider integrations, private deployment and support. Sponsorship can help fund the shared tools. Those are hypotheses I want to validate with real users, rather than invent a subscription price for the post.&lt;/p&gt;

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

&lt;p&gt;End-to-end video walkthrough coming soon....&lt;/p&gt;

&lt;p&gt;The walkthrough follows a complete journey:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with an empty Miilo.&lt;/li&gt;
&lt;li&gt;Connect four named systems.&lt;/li&gt;
&lt;li&gt;Switch between Compact and Detail, then filter what needs attention.&lt;/li&gt;
&lt;li&gt;Open a review task and inspect its notes, check result and colored file changes.&lt;/li&gt;
&lt;li&gt;Ask Gemma to explain it.&lt;/li&gt;
&lt;li&gt;Confirm a supported approval and watch the observed state update.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The recording uses curated task fixtures with &lt;strong&gt;live Gemma inference&lt;/strong&gt;. The approval shown affects an isolated demonstration provider. Existing-session observation is tested separately; the demo does not imply that every coding harness exposes approval controls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preview clips
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://miilo.tech/previews/tokyo-night-orbit.mp4" rel="noopener noreferrer"&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%2Fqgcncff56hbloe8jnauw.png" alt="Tokyo Night: robots in Orbit mode" width="720" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://miilo.tech/previews/tokyo-night-orbit.mp4" rel="noopener noreferrer"&gt;Watch Tokyo Night — Orbit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://miilo.tech/previews/osaka-jade-quiet.mp4" rel="noopener noreferrer"&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%2F5a5rklt3pagrh8f0zyiv.png" alt="Osaka Jade: the quieter robot fleet view" width="720" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://miilo.tech/previews/osaka-jade-quiet.mp4" rel="noopener noreferrer"&gt;Watch Osaka Jade — Quiet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://miilo.tech/previews/task-inspection.mp4" rel="noopener noreferrer"&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%2Fp46uagsh9lhz692iyfaq.png" alt="Task inspection: checks, evidence and colored file changes" width="720" height="760"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://miilo.tech/previews/task-inspection.mp4" rel="noopener noreferrer"&gt;Watch task inspection&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These three short previews use sample systems and tasks. The separate end-to-end mobile recording will show the longer onboarding and explanation journey.&lt;/p&gt;

&lt;p&gt;Project home: &lt;a href="https://miilo.tech" rel="noopener noreferrer"&gt;miilo.tech&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;The entry's main pieces are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/miilo-ai/miilo-cli" rel="noopener noreferrer"&gt;Miilo CLI&lt;/a&gt;: standalone observation of explicitly selected existing sessions, plus optional remote connections.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/miilo-ai/miilo-owl-widgets" rel="noopener noreferrer"&gt;Miilo widgets&lt;/a&gt;: the shared robot experience, Rust helper and Omalauncher integration.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/miilo-ai/miilo-omarchy-plugin" rel="noopener noreferrer"&gt;Miilo for Omarchy&lt;/a&gt;: the native desktop plugin.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/miilo-ai/miilo-sdk" rel="noopener noreferrer"&gt;Miilo SDK&lt;/a&gt;: the provider contracts and shared foundation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/miilo-ai/homebrew-cli" rel="noopener noreferrer"&gt;Homebrew tap&lt;/a&gt;: installation for macOS and Linux.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install the CLI with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;miilo-ai/cli/miilo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Versioned downloads are available for &lt;a href="https://github.com/miilo-ai/miilo-cli/releases/tag/v0.2.2" rel="noopener noreferrer"&gt;CLI v0.2.2&lt;/a&gt;, &lt;a href="https://github.com/miilo-ai/miilo-sdk/releases/tag/v0.2.0" rel="noopener noreferrer"&gt;SDK v0.2.0&lt;/a&gt;, &lt;a href="https://github.com/miilo-ai/miilo-owl-widgets/releases/tag/v1.4.0" rel="noopener noreferrer"&gt;widgets v1.4.0&lt;/a&gt;, and &lt;a href="https://github.com/miilo-ai/miilo-omarchy-plugin/releases/tag/v0.1.0" rel="noopener noreferrer"&gt;Omarchy plugin v0.1.0&lt;/a&gt;. Source availability does not imply an approved marketplace or AUR listing.&lt;/p&gt;

&lt;p&gt;The broader Miilo planning and SDK predate this challenge. During Week 2, I built the standalone observer CLI, launcher experience, native Omarchy plugin, and public website, building on that earlier foundation. The CLI repository began October 13, widgets October 14, and the separate native plugin October 16. The website is now live at miilo.tech, hosted on DigitalOcean. Many thanks to our sponsors!&lt;/p&gt;

&lt;p&gt;Omalauncher, OWL, and the upstream Quickshell renderer provide the platform. The vendored renderer retains its upstream notices and source provenance. Miilo adds observation integration, original robot behavior, workflow inspection, and model-backed explanations.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Observation starts with a deliberate choice
&lt;/h3&gt;

&lt;p&gt;Miilo observes explicitly selected existing local sessions. Codex and Cursor are available in the packaged CLI; five additional adapters are merged for CLI 0.3.0, with release packages pending. Discovery does not start an agent or silently enroll everything it finds. Local observation can run without a Miilo account, a cloud backend or a listening HTTP service.&lt;/p&gt;

&lt;p&gt;For remote automation, users can explicitly connect a provider endpoint. A provider can supply richer workflow state, evidence and supported actions. Local transcript activity alone does not establish authoritative approval or execution state.&lt;/p&gt;

&lt;p&gt;Rust owns collection and helper behavior. Domain/application boundaries keep that logic separate from filesystem, HTTP, process and presentation adapters. The native CLI is complemented by a Node client; OWL and QML provide the launcher and desktop presentation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Seven local agents, one crew
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Compatibility update, October 18, 2026:&lt;/strong&gt; Miilo now has seven local observation adapters in source. Here is when each was added to the Miilo CLI, alongside the agent versions we checked:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Added in Miilo CLI&lt;/th&gt;
&lt;th&gt;Validated agent version or source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;0.2.0&lt;/td&gt;
&lt;td&gt;Existing local sessions and live observation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;0.2.0&lt;/td&gt;
&lt;td&gt;Existing local transcripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;0.3.0&lt;/td&gt;
&lt;td&gt;2.1.300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenCode&lt;/td&gt;
&lt;td&gt;0.3.0&lt;/td&gt;
&lt;td&gt;1.19.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Devin CLI&lt;/td&gt;
&lt;td&gt;0.3.0&lt;/td&gt;
&lt;td&gt;3000.11.92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok CLI&lt;/td&gt;
&lt;td&gt;0.3.0&lt;/td&gt;
&lt;td&gt;1.0.60, local history format 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hermes&lt;/td&gt;
&lt;td&gt;0.3.0&lt;/td&gt;
&lt;td&gt;0.22.2, interactive CLI sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Codex and Cursor support is already released. &lt;strong&gt;CLI 0.3.0 is merged, with versioned release assets pending.&lt;/strong&gt; The matching launcher updates are &lt;strong&gt;widgets/helper 1.5.0&lt;/strong&gt; and &lt;strong&gt;native Omarchy plugin 0.3.0&lt;/strong&gt;, also merged and awaiting publication. Check the repository release links above before updating.&lt;/p&gt;

&lt;p&gt;Claude Code, OpenCode and Hermes passed real reply and live follow-up checks. Devin CLI and Grok CLI passed real-reply and selected-session observation checks. Cursor passed real transcript discovery/import; its incremental updates have fixture coverage, while a fresh live follow-up remains unverified.&lt;/p&gt;

&lt;p&gt;These are local session adapters, not integrations with every similarly named cloud product. Claude Desktop/Cowork, Devin Cloud, Grok web chat and Hermes one-shot/messaging sessions are outside this scope. The adapters observe bounded visible assistant activity; they do not grant approval, rejection or retry authority. Full version-qualified details live in the &lt;a href="https://github.com/miilo-ai/miilo-cli/blob/main/docs/AGENT_COMPATIBILITY.md" rel="noopener noreferrer"&gt;CLI compatibility matrix&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  The robots share one visual language
&lt;/h3&gt;

&lt;p&gt;Compact and Detail use the same observed state. A system's robot remains recognizable as you move into its tasks.&lt;/p&gt;

&lt;p&gt;The orbital tracks and moving lights share their geometry, so the lights stay on their paths. Sleeping systems have muted rings. Different offsets keep the crew from moving in perfect synchronization. A brief electrical zap adds personality to active work.&lt;/p&gt;

&lt;p&gt;These effects are decorative. The task notes and evidence remain the basis for decisions. Reduced and disabled motion options are available.&lt;/p&gt;
&lt;h3&gt;
  
  
  Gemma explains the evidence
&lt;/h3&gt;

&lt;p&gt;For the recorded explanation, I served &lt;strong&gt;Google Gemma 3 12B IT&lt;/strong&gt; on infrastructure I control, using Transformers/PyTorch behind an OpenAI-compatible endpoint. The task’s Explain button calls its provider’s explanation capability; the demonstration provider delegates that request to Gemma. General text chat uses a separate configurable helper path.&lt;/p&gt;

&lt;p&gt;The explanation path supplies bounded context for the selected task: its observed state, review notes, linked evidence and available file changes. Gemma turns that context into a short explanation.&lt;/p&gt;

&lt;p&gt;The model does not receive authority to approve a task. Where a provider advertises an eligible action, the helper rechecks the observed revision and requires confirmation before submitting it. Changed revisions are rejected, and mutation requests are not automatically retried.&lt;/p&gt;

&lt;p&gt;That boundary matters when the answer sounds convincing. A generated explanation is still an explanation; provider state and evidence establish what happened.&lt;/p&gt;
&lt;h3&gt;
  
  
  Test the actual paths
&lt;/h3&gt;

&lt;p&gt;Validation included &lt;strong&gt;264 official OWL tile/screen evaluations&lt;/strong&gt;, &lt;strong&gt;36 desktop scenarios&lt;/strong&gt;, &lt;strong&gt;54 exact portrait comparisons&lt;/strong&gt;, and the plugin's 24 Node tests. Rust tests cover HTTP behavior, stale revisions and the explanation deadline.&lt;/p&gt;

&lt;p&gt;We also clicked through the actual Android emulator. That exposed a real timing issue: a valid model response could exceed the ordinary eight-second request deadline. Read-only explanations now have a twenty-second deadline, while observation and action deadlines stay unchanged.&lt;/p&gt;

&lt;p&gt;The published CLI artifacts were also checked on macOS, Linux and Windows, and the public Homebrew installation was validated on macOS and Linux. Installed Omarchy acceptance remains a separate host check. Voice is deferred from this release.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I can choose where inference runs.&lt;/strong&gt; Self-hosting Gemma lets me keep the explanation service on infrastructure I control. It avoids a mandatory third-party model API bill, although hardware, electricity and operation still cost money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model is replaceable.&lt;/strong&gt; Users can configure an OpenAI-compatible endpoint instead of depending on my model server. Gemma is the open-weight model demonstrated here; it is not a requirement to use one particular hosted AI vendor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The observation tools can stand on their own.&lt;/strong&gt; A developer can use the local CLI without signing up for a managed service. Shared provider contracts give other people a way to add integrations.&lt;/p&gt;

&lt;p&gt;These choices also make the business question more interesting: can useful open tools lead to sustainable integration and support work?&lt;/p&gt;

&lt;p&gt;Privacy still requires care. Selected summaries and evidence can contain sensitive information. Users need to understand the context they share with a configured model; self-hosting alone is not a guarantee of security.&lt;/p&gt;

&lt;p&gt;Development and this write-up used AI assistance. The demonstrated workflows were reviewed and exercised, including the actual emulator interaction.&lt;/p&gt;
&lt;h2&gt;
  
  
  My First User
&lt;/h2&gt;

&lt;p&gt;Released on October 17, 2026. Here are the latest download counts across the projects:&lt;/p&gt;


&lt;div class="ltag-row"&gt;
  &lt;div class="ltag-col"&gt;
  &lt;center&gt;&lt;a href="https://github.com/miilo-ai/miilo-cli/releases" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fdownloads%2Fmiilo-ai%2Fmiilo-cli%2Ftotal.png%3Flabel%3DCLI" alt="CLI release downloads" width="52" height="20"&gt;&lt;/a&gt;&lt;/center&gt;

&lt;/div&gt;

&lt;div class="ltag-col"&gt;
  &lt;center&gt;&lt;a href="https://github.com/miilo-ai/miilo-sdk/releases" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fdownloads%2Fmiilo-ai%2Fmiilo-sdk%2Ftotal.png%3Flabel%3DSDK" alt="SDK release downloads" width="56" height="20"&gt;&lt;/a&gt;&lt;/center&gt;

&lt;/div&gt;

&lt;div class="ltag-col"&gt;
  &lt;center&gt;&lt;a href="https://github.com/miilo-ai/miilo-omarchy-plugin/releases" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fdownloads%2Fmiilo-ai%2Fmiilo-omarchy-plugin%2Ftotal.png%3Flabel%3DOmarchy" alt="Omarchy release downloads" width="76" height="20"&gt;&lt;/a&gt;&lt;/center&gt;

&lt;/div&gt;

&lt;div class="ltag-col"&gt;
  &lt;center&gt;&lt;a href="https://github.com/miilo-ai/miilo-owl-widgets/releases" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fdownloads%2Fmiilo-ai%2Fmiilo-owl-widgets%2Ftotal.png%3Flabel%3DWidgets" alt="Widgets release downloads" width="86" height="20"&gt;&lt;/a&gt;&lt;/center&gt;

&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;&lt;em&gt;GitHub release-asset downloads, including supporting files, repeat downloads and automation. These are not unique users or verified installations.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; Gemma 3 12B IT performs the live task explanation shown in the demo. It receives selected workflow context and evidence through an OpenAI-compatible service running on infrastructure I control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use of DigitalOcean:&lt;/strong&gt; The public Miilo website is deployed on DigitalOcean App Platform at &lt;a href="https://miilo.tech" rel="noopener noreferrer"&gt;miilo.tech&lt;/a&gt;, with a &lt;a href="https://miilo-web-app-zbntf.ondigitalocean.app/" rel="noopener noreferrer"&gt;DigitalOcean starter URL&lt;/a&gt;. The demonstrated Gemma inference runs on separate, self-hosted infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use of a .tech Domain:&lt;/strong&gt; I claimed &lt;strong&gt;miilo.tech&lt;/strong&gt; through the Hacktoberfest offer. It is now the public website address, serving the DigitalOcean deployment over HTTPS: &lt;a href="https://miilo.tech" rel="noopener noreferrer"&gt;miilo.tech&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>Foliage Hunter: a Colorado leaf guide on a fine-tuned Gemma 3</title>
      <dc:creator>Eidan Rosado</dc:creator>
      <pubDate>Sat, 10 Oct 2026 22:33:21 +0000</pubDate>
      <link>https://dev.to/edyvision/foliage-hunter-a-colorado-leaf-guide-on-a-fine-tuned-gemma-3-built-to-keep-the-screen-short-4iko</link>
      <guid>https://dev.to/edyvision/foliage-hunter-a-colorado-leaf-guide-on-a-fine-tuned-gemma-3-built-to-keep-the-screen-short-4iko</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;Foliage Hunter is a pocket leaf guide for peeping fantastic fall foliage. You walk, you find a leaf, you snap it. A Gemma 3 model fine-tuned on 18 Colorado species names the tree, Magpie TTS speaks a short guideline, and the find gets pinned to a winding trail of everything you have spotted this season. The screen is meant to be the shortest part of the walk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F98976bd649ildrzeel0j.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F98976bd649ildrzeel0j.gif" alt="Foliage Hunter demo: snap a leaf, hear the guide, watch the trail grow" width="360" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is for anyone on the Front Range who wants to know what they are looking at without stopping to read. The loop is deliberately short. One tap to shoot, a few seconds of a leaf wreath spinning, and the guide starts talking before the whole reply has even been synthesized. You keep walking. Asking the guide free-form questions about the current find (mic in, spoken answer out) is planned for a follow-up; this release identifies and speaks the find, and the mic path only works against the built-in demo guide.&lt;/p&gt;

&lt;p&gt;The part that makes it a Colorado guide and not a generic plant app is a LoRA adapter I trained this week on iNaturalist photos of the species you actually meet here: &lt;em&gt;quaking aspen, narrowleaf cottonwood, Gambel oak, boxelder, Rocky Mountain maple, chokecherry, serviceberry, peachleaf willow&lt;/em&gt;, plus the urban Front Range imports (&lt;em&gt;green ash, Siberian elm, silver maple&lt;/em&gt;) and the fall-color classics (&lt;em&gt;sugar maple, northern red oak, American sweetgum, ginkgo&lt;/em&gt;), and the three conifers that frame several trails (&lt;em&gt;ponderosa, Douglas-fir, blue spruce&lt;/em&gt;).&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trail landing&lt;/th&gt;
&lt;th&gt;Identification&lt;/th&gt;
&lt;th&gt;Past finds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2Fyrm1z97s8swj7oj14igg.png" alt="Trail landing" width="800" height="1734"&gt;&lt;/td&gt;
&lt;td&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%2Fqws7aukrmqjclmgzems4.png" alt="Quaking aspen identification" width="800" height="1734"&gt;&lt;/td&gt;
&lt;td&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%2F0tligmudu51gr69cxtwz.png" alt="Past finds" width="800" height="1734"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Below are six test finds. The clips are Magpie TTS output the phone played. DEV (like GitHub READMEs) does not allow inline &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt;, so the players live on the project Pages site: &lt;strong&gt;&lt;a href="https://edyvision.github.io/foliage-hunter/" rel="noopener noreferrer"&gt;Hear the guide&lt;/a&gt;&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;Find&lt;/th&gt;
&lt;th&gt;Listen&lt;/th&gt;
&lt;th&gt;What the guide said&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Quaking aspen&lt;/td&gt;
&lt;td&gt;&lt;a href="https://edyvision.github.io/foliage-hunter/#quaking-aspen" rel="noopener noreferrer"&gt;Play 16 s&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;That looks like quaking aspen. Nice find. Leaf colors include ember, bronze, gold, and rust. Famous for fluttering leaves and Colorado gold in fall. Bark and buds feed wildlife, not a human snack.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sugar maple&lt;/td&gt;
&lt;td&gt;&lt;a href="https://edyvision.github.io/foliage-hunter/#sugar-maple" rel="noopener noreferrer"&gt;Play 14 s&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;That looks like sugar maple. Nice find. Leaf colors include amber, ember, rust, and gold. Famous for tapped sap made into maple syrup. Leaves and raw parts are not snacks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;American sweetgum&lt;/td&gt;
&lt;td&gt;&lt;a href="https://edyvision.github.io/foliage-hunter/#american-sweetgum" rel="noopener noreferrer"&gt;Play 16 s&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;That looks like American sweetgum. Nice find. Leaf colors include bronze, amber, rust, and ember. Famous for star-shaped leaves and spiky gum balls. Not a food plant.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Northern red oak&lt;/td&gt;
&lt;td&gt;&lt;a href="https://edyvision.github.io/foliage-hunter/#northern-red-oak" rel="noopener noreferrer"&gt;Play 16 s&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;That looks like northern red oak. Nice find. Leaf colors include scarlet, burgundy, rust, and amber. Famous for pointed-lobed leaves and strong fall red. Acorns are high-tannin and need processing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ginkgo&lt;/td&gt;
&lt;td&gt;&lt;a href="https://edyvision.github.io/foliage-hunter/#ginkgo" rel="noopener noreferrer"&gt;Play 16 s&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;That looks like ginkgo. Nice find. Leaf colors include amber, bronze, ember, and burgundy. Famous as a living fossil with fan-shaped leaves. Seeds need careful prep; the fleshy coat is foul and irritating.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boxelder&lt;/td&gt;
&lt;td&gt;&lt;a href="https://edyvision.github.io/foliage-hunter/#boxelder" rel="noopener noreferrer"&gt;Play 13 s&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;That looks like boxelder. Nice find. Leaf colors include amber, olive, bronze, and burgundy. A maple relative common in yards and washes. Grown for shade, not fruit.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is no hosted URL on purpose. The app runs in Expo Go on your phone and talks to a model you host. The README's "Run it" and "Taking it outside" sections get you from clone to trail.&lt;/p&gt;

&lt;h3&gt;
  
  
  I took it outside
&lt;/h3&gt;

&lt;p&gt;I took it out for about ten minutes in Castle Rock, Colorado, on the afternoon of October 10. Phone on cellular, Mac at home on the tailnet, guide server in the lab. Each find now also stores an on-device place name (city and state from the phone's geocoder, no Google Maps API), so the drawer can show where I was standing when I snapped the leaf. Two finds that stuck with me: American sweetgum (star leaves turning hard) and green ash on a street strip. Both pinned as Castle Rock, CO.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;American sweetgum&lt;/th&gt;
&lt;th&gt;Green ash&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2Fi5roacjfesyrwq8xn15m.PNG" alt="American sweetgum in Castle Rock" width="800" height="1734"&gt;&lt;/td&gt;
&lt;td&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%2Fz5ha7471h5lzkgf65twl.PNG" alt="Green ash in Castle Rock" width="800" height="1734"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first attempt failed in the field. Every photo came back as a 500 from the gateway, and the server log said &lt;code&gt;PIL.UnidentifiedImageError&lt;/code&gt;. Real iPhones shoot HEIC and the simulator never did, so the guide server had never seen one. The fix was to re-encode on the phone with &lt;code&gt;expo-image-manipulator&lt;/code&gt; (resize to 1280 px, JPEG at 0.82) before upload, which also cut the payload to a fraction of the original. After that the finds in the table above came in one after another, and the trail filled in as I went.&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/EdyVision" rel="noopener noreferrer"&gt;
        EdyVision
      &lt;/a&gt; / &lt;a href="https://github.com/EdyVision/foliage-hunter" rel="noopener noreferrer"&gt;
        foliage-hunter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Hacktoberfest Challenge Week 1 - Outdoor Activity Application with Open Weight Models Used
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Foliage Hunter&lt;/h1&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
  &lt;a href="https://dev.to/challenges/hf26" rel="nofollow"&gt;&lt;img alt="Hacktoberfest Open-Source AI Challenge: Week 1" src="https://camo.githubusercontent.com/1f8b049c2742bdf4c9ae2c3f830d4d6c55cfd6569931412d72c59e5dad6883f2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f62657266657374253230323032362d5765656b25323031253230254332254237253230546f75636825323047726173732d6666366233353f6c6f676f3d6861636b746f62657266657374266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
  &lt;a href="https://huggingface.co/EdyVision/foliage-hunter-gemma3-12b" rel="nofollow noopener noreferrer"&gt;&lt;img alt="Gemma 3 12B LoRA adapter" src="https://camo.githubusercontent.com/fdf69df683e53f7efa4163e3301807f242bcc52e5ceaab564e9ab4e88df4e4f2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f47656d6d61253230332532303132422d4c6f5241253230616461707465722d3432383546343f6c6f676f3d676f6f676c65266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
  &lt;a href="https://huggingface.co/EdyVision/foliage-hunter-gemma3-12b" rel="nofollow noopener noreferrer"&gt;&lt;img alt="Hugging Face" src="https://camo.githubusercontent.com/921e98589d58b299ca1958ac81a46e9115df6a8f3402b3376ca85934d7204af6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f25463025394625413425393725323048756767696e67253230466163652d6d6f64656c2532302532422532306578706572696d656e74732d464644323145"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5cfc84905ac3a28e3228e165d84baff692f9a749c7aacc2d7d5ace8836b5547a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f68656c642d2d6f757425323061636375726163792d38352e3125323525323028323435253246323838292d324436413446"&gt;&lt;img alt="Held-out accuracy" src="https://camo.githubusercontent.com/5cfc84905ac3a28e3228e165d84baff692f9a749c7aacc2d7d5ace8836b5547a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f68656c642d2d6f757425323061636375726163792d38352e3125323525323028323435253246323838292d324436413446"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e"&gt;&lt;img alt="License: MIT" src="https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/94a8ef97cd08766623f6d24e0f7c8326ea32ed17c494644e77252252f5ab8b35/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4578706f2d53444b25323035372d3030303032303f6c6f676f3d6578706f266c6f676f436f6c6f723d7768697465"&gt;&lt;img alt="Expo SDK 57" src="https://camo.githubusercontent.com/94a8ef97cd08766623f6d24e0f7c8326ea32ed17c494644e77252252f5ab8b35/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4578706f2d53444b25323035372d3030303032303f6c6f676f3d6578706f266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b5abaee4ba978783202796e0af3a6dafd515f398d613f13e625dc1844e3ab8ca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52656163742532304e61746976652d302e38362d3631444146423f6c6f676f3d7265616374266c6f676f436f6c6f723d626c61636b"&gt;&lt;img alt="React Native 0.86" src="https://camo.githubusercontent.com/b5abaee4ba978783202796e0af3a6dafd515f398d613f13e625dc1844e3ab8ca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52656163742532304e61746976652d302e38362d3631444146423f6c6f676f3d7265616374266c6f676f436f6c6f723d626c61636b"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b5a6d8987c7b2df75e8f265cbd77573bdec14f2716c3abce3bcd345d35b5622c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742d7374726963742d3331373843363f6c6f676f3d74797065736372697074266c6f676f436f6c6f723d7768697465"&gt;&lt;img alt="TypeScript" src="https://camo.githubusercontent.com/b5a6d8987c7b2df75e8f265cbd77573bdec14f2716c3abce3bcd345d35b5622c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742d7374726963742d3331373843363f6c6f676f3d74797065736372697074266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6c229bfd12ef3317c2bb8d82bc114da606c79c856d6f482e344d5f9e783f17e2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f504546542d4c6f52412532307225334431362d79656c6c6f77"&gt;&lt;img alt="PEFT LoRA" src="https://camo.githubusercontent.com/6c229bfd12ef3317c2bb8d82bc114da606c79c856d6f482e344d5f9e783f17e2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f504546542d4c6f52412532307225334431362d79656c6c6f77"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0222bfe6d24e595bb400a431eca9a8dda4e6d2202e4e2c6c383be134c2aa811b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5261792d4a6f62732532304150492d3032384346303f6c6f676f3d726179266c6f676f436f6c6f723d7768697465"&gt;&lt;img alt="Ray" src="https://camo.githubusercontent.com/0222bfe6d24e595bb400a431eca9a8dda4e6d2202e4e2c6c383be134c2aa811b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5261792d4a6f62732532304150492d3032384346303f6c6f676f3d726179266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6caca6083d744368118eb94aae1173864192c3d4e27d8fd040c306998579f507/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4e56494449412532304e654d6f2d4d61677069652532305454532d3736423930303f6c6f676f3d6e7669646961266c6f676f436f6c6f723d7768697465"&gt;&lt;img alt="NVIDIA NeMo" src="https://camo.githubusercontent.com/6caca6083d744368118eb94aae1173864192c3d4e27d8fd040c306998579f507/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4e56494449412532304e654d6f2d4d61677069652532305454532d3736423930303f6c6f676f3d6e7669646961266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/58089870afd2851c849861de97fa48acdb097efb843ef6d6da0292b229b045ec/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6974654c4c4d2d4f70656e41492d2d636f6d70617469626c652d364535364346"&gt;&lt;img alt="LiteLLM" src="https://camo.githubusercontent.com/58089870afd2851c849861de97fa48acdb097efb843ef6d6da0292b229b045ec/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6974654c4c4d2d4f70656e41492d2d636f6d70617469626c652d364535364346"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/cc98dbbf471a0cac0b741a71088e5d99569958e7456d1375ed98dd9920439e59/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f466173744150492d67756964652532307365727665722d3030393638383f6c6f676f3d66617374617069266c6f676f436f6c6f723d7768697465"&gt;&lt;img alt="FastAPI" src="https://camo.githubusercontent.com/cc98dbbf471a0cac0b741a71088e5d99569958e7456d1375ed98dd9920439e59/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f466173744150492d67756964652532307365727665722d3030393638383f6c6f676f3d66617374617069266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a96566fc6436cfdd8d815c38d64b839a485a5ab977cd1a1eac75b78b709e35a9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f694e61747572616c6973742d72657365617263682d2d6772616465253230636f727075732d373441433030"&gt;&lt;img alt="iNaturalist" src="https://camo.githubusercontent.com/a96566fc6436cfdd8d815c38d64b839a485a5ab977cd1a1eac75b78b709e35a9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f694e61747572616c6973742d72657365617263682d2d6772616465253230636f727075732d373441433030"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/8fbec73a821cc26857f6a3af57d0d40d37662a0aef7733c82e81b37dd336899e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5461696c7363616c652d70686f6e652532302545322538362539342532306c61622d3234323432343f6c6f676f3d7461696c7363616c65266c6f676f436f6c6f723d7768697465"&gt;&lt;img alt="Tailscale" src="https://camo.githubusercontent.com/8fbec73a821cc26857f6a3af57d0d40d37662a0aef7733c82e81b37dd336899e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5461696c7363616c652d70686f6e652532302545322538362539342532306c61622d3234323432343f6c6f676f3d7461696c7363616c65266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Foliage Hunter is a pocket leaf guide for Colorado's fall. You walk, you find a leaf, you snap it. A Gemma 3 model fine-tuned on Colorado species names the tree, a guide voice tells you what you are looking at, and the find is pinned to a winding trail of everything you have spotted this season. The screen is meant to be the shortest part of the walk.&lt;/p&gt;
&lt;div&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/EdyVision/foliage-hunter/docs/media/demo.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2FEdyVision%2Ffoliage-hunter%2FHEAD%2Fdocs%2Fmedia%2Fdemo.gif" alt="Foliage Hunter demo: snap a leaf, hear the guide, watch the trail grow" width="360"&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why this exists&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/hf26" rel="nofollow"&gt;Hacktoberfest Open-Source AI Challenge: Week 1&lt;/a&gt; on DEV (October 5–11, 2026). The week's theme was &lt;strong&gt;Touch Grass&lt;/strong&gt;: build something with open-source AI at its core that gets people off the screen and into the world. Fall foliage was literally on the prompt's list of examples, and the Front Range was peaking. Entered under &lt;code&gt;#hf26challenge&lt;/code&gt; and the &lt;strong&gt;Best Use of Gemma&lt;/strong&gt; category.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why open models matter here&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A regional model is only possible with&lt;/strong&gt;…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/EdyVision/foliage-hunter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;MIT. The adapter, the split manifests, and the held-out results are public on Hugging Face:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adapter: &lt;a href="https://huggingface.co/EdyVision/foliage-hunter-gemma3-12b" rel="noopener noreferrer"&gt;EdyVision/foliage-hunter-gemma3-12b&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Experiments: &lt;a href="https://huggingface.co/datasets/EdyVision/foliage-hunter-experiments" rel="noopener noreferrer"&gt;EdyVision/foliage-hunter-experiments&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Two open models are in the live loop. Everything else is on-device code or a curated catalog.&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;What runs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name the leaf&lt;/td&gt;
&lt;td&gt;Gemma 3 12B IT + LoRA (vision identify only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speak the guide line&lt;/td&gt;
&lt;td&gt;NVIDIA Magpie TTS 357M + NanoCodec (GGUF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Species notes&lt;/td&gt;
&lt;td&gt;Curated catalog on the phone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Leaf colors&lt;/td&gt;
&lt;td&gt;On-device JPEG palette&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Place on the find&lt;/td&gt;
&lt;td&gt;Phone GPS + platform reverse geocode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free mic conversation&lt;/td&gt;
&lt;td&gt;Planned (&lt;code&gt;/v1/voice/turn&lt;/code&gt;); demo guide only in this release&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  phone (Expo Go)                       home lab
 ┌───────────────────┐   Tailscale    ┌──────────────────────────────────────┐
 │ camera / library  │ ─────────────▶ │ LiteLLM  (alias: gemma-foliage)      │
 │ jpeg palette      │                │   └─▶ guide-server (FastAPI + PEFT)  │
 │ species traits    │ ◀───────────── │        Gemma 3 12B IT + LoRA r=16    │
 │ Magpie playback   │   audio/wav    │ nemo-speech (GGUF)                   │
 │ GPS + place name  │                │   Magpie TTS 357M                    │
 │ trail + history   │                │                                      │
 └───────────────────┘                └──────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The eyes: Gemma 3 12B IT plus a LoRA.&lt;/strong&gt; I pulled 80 research-grade iNaturalist photos per species through the public API (Colorado place filter for the local tiers), 1,440 photos from 729 observations, and split them 80/20 stratified per species. Training is PEFT LoRA on &lt;code&gt;all-linear&lt;/code&gt; modules, r=16, alpha=32, 600 steps, lr 2e-4, with the loss masked to the answer tokens only. The prompt is closed-set on purpose. A general VLM will happily say "maple" for a boxelder; the task I actually have is "which of these 18 is it":&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Identify the plant in the photo.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;Choose exactly one common name from this list: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reply with that common name only. No punctuation, no extra words.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The job runs through the Ray Jobs API on a DGX Spark in my home lab. Evaluation is generate-and-match on the 288 held-out photos: &lt;strong&gt;85.1% (245/288)&lt;/strong&gt;. The adapter is 284 MB, base weights are not re-uploaded, and the model card carries the Gemma terms. Gemma is the specialist here: it names the leaf and nothing else. When the Gemma 4 training path on this GPU is unblocked, the LoRA job takes a new &lt;code&gt;BASE_MODEL_ID&lt;/code&gt; and the app will not know the difference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The voice: NVIDIA Magpie TTS, as GGUF.&lt;/strong&gt; Speech out is Magpie TTS Multilingual 357M under NeMo-Speech.cpp on the same box. After identify, the app constructs a short spoken line from the species name, on-device palette colors, and a curated trait note, then streams it sentence by sentence so the first clip plays while the rest is still being synthesized. Clips save next to the find; reopening a past find replays with no network. A free conversation about the current find (speech in, spoken answer out) is next: that needs a real &lt;code&gt;/v1/voice/turn&lt;/code&gt; on the guide path. This release does not ship that route, so the mic only answers through the built-in demo guide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The app: Expo SDK 57, hexagonal.&lt;/strong&gt; &lt;code&gt;ports/&lt;/code&gt; holds the interfaces (&lt;code&gt;GuideClient&lt;/code&gt;, &lt;code&gt;Speech&lt;/code&gt;, &lt;code&gt;Camera&lt;/code&gt;, &lt;code&gt;PaletteReader&lt;/code&gt;, &lt;code&gt;FindHistory&lt;/code&gt;, &lt;code&gt;Locator&lt;/code&gt;), &lt;code&gt;adapters/&lt;/code&gt; the Expo and HTTP implementations, &lt;code&gt;application/&lt;/code&gt; the use-cases, and one file (&lt;code&gt;composition/wire.ts&lt;/code&gt;) reads env vars and assembles everything. That is what makes the models swappable; the identify flow never learns which TTS or which VLM it is talking to. Leaf colors are extracted on the phone from the JPEG bytes, place comes from the phone's location APIs, and without a server URL the app falls back to a built-in demo guide so the UI still works offline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Speech&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;SpokenAudio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;SpeakOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;discardSaved&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uris&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The path outside: Tailscale.&lt;/strong&gt; The phone and the Mac sit on the same tailnet. &lt;code&gt;make start-ts&lt;/code&gt; makes Metro advertise the Mac's &lt;code&gt;100.x&lt;/code&gt; address so the Expo Go QR code works on cellular, and the guide URL points at the lab's tailnet host. No port forwarding, no public endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Things that bit me
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HEIC.&lt;/strong&gt; Covered above. Conversion was necessary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License filters leak.&lt;/strong&gt; My corpus builder accepts &lt;code&gt;cc0&lt;/code&gt;, &lt;code&gt;cc-by&lt;/code&gt;, &lt;code&gt;cc-by-sa&lt;/code&gt;, &lt;code&gt;cc-by-nc&lt;/code&gt;, and the manifests still contain 72 &lt;code&gt;-nd&lt;/code&gt; and &lt;code&gt;-nc-sa&lt;/code&gt; photos because the match was too loose. Images were private training only and are not redistributed, and the real per-photo breakdown is now in the README and the dataset card.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Free conversation with the guide about the leaf you are holding: mic audio in, a spoken answer that can describe what the model sees, with the current find as context. The UI affordance is already there; the server route and ASR wiring are the missing piece.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;A regional model only exists because the base is open.&lt;/strong&gt; No hosted vision API is going to learn the difference between a Gambel oak and a northern red oak on a Colorado trail for me. With &lt;code&gt;google/gemma-3-12b-it&lt;/code&gt; as an open base, a 284 MB adapter and 1,440 photos turn a general model into a local one, and I can publish the adapter and every split manifest so someone in Vermont or Oregon can retrain it for their species in an afternoon. An API with training capabilities can get us to this point, but for self-funded researchers and students, that can become costly as they learn their edge cases or restrictions they may need to set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Photos and location never leave hardware I control.&lt;/strong&gt; Every leaf photo is taken somewhere, usually near where I live. Inference runs in my own lab and the phone reaches it over a private tailnet. Place names for finds are resolved on the phone. No third-party Maps key, no per-image bill, no photo or location on a server I do not own. For more private applications such as health and wellness apps, this is everything to researchers. Before we publish sanitized datasets to the cloud, our data must live somewhere and be processed somehow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything is swappable.&lt;/strong&gt; The app speaks plain OpenAI-compatible HTTP. Point &lt;code&gt;EXPO_PUBLIC_GUIDE_MODEL&lt;/code&gt; at a different alias and nothing in the client changes. When the device TTS sounded like a GPS unit, I swapped in Magpie behind the same &lt;code&gt;Speech&lt;/code&gt; port in an afternoon. When the Gemma 4 training path on this GPU is unblocked, the LoRA job takes a new &lt;code&gt;BASE_MODEL_ID&lt;/code&gt; and the app will not know the difference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It costs nothing to run and it degrades gracefully.&lt;/strong&gt; Marginal cost per identification is electricity. Color extraction is on-device, saved clips replay offline, and the demo guide keeps the UI alive with no network. The screen stays short because nothing in the loop is waiting on a vendor.&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 3 12B IT is the model that identifies the leaf: closed-set vision over 18 Colorado species, LoRA published, served from my own hardware. Magpie handles speech. The short screen time on the trail is the product of that split.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built solo. Thanks to the iNaturalist observers whose photos taught the model what a Colorado leaf looks like; every one of them is credited by observation ID in the experiments dataset.&lt;/p&gt;

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