<?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: Kshitij</title>
    <description>The latest articles on DEV Community by Kshitij (@kernelkain).</description>
    <link>https://dev.to/kernelkain</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%2F3953019%2F39f656d7-121d-4b3f-b768-8b352486b929.jpg</url>
      <title>DEV Community: Kshitij</title>
      <link>https://dev.to/kernelkain</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kernelkain"/>
    <language>en</language>
    <item>
      <title>Japan turning footsteps into Electricity, I turned them into Music.</title>
      <dc:creator>Kshitij</dc:creator>
      <pubDate>Mon, 12 Oct 2026 02:31:19 +0000</pubDate>
      <link>https://dev.to/kernelkain/japan-turning-footsteps-into-electricity-i-turned-them-into-music-4h05</link>
      <guid>https://dev.to/kernelkain/japan-turning-footsteps-into-electricity-i-turned-them-into-music-4h05</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;I built &lt;strong&gt;Footwork&lt;/strong&gt;, a music experience where going for a walk becomes part of the creative process. Instead of asking you to stare at a screen, it turns the way you move - your pace, pauses, turns, and loops into a short instrumental called a &lt;strong&gt;Soundprint&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Using it is intentionally simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tap &lt;strong&gt;Start walking&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Put your phone away and explore.&lt;/li&gt;
&lt;li&gt;End the walk and listen to what your movement created.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every walk has its own rhythm. You might slow down to cross a road, make a sudden turn, speed up along an open stretch, or loop back toward somewhere you have already been. Footwork turns moments like these into musical decisions, such as changes in energy, transitions between sections, and brief pauses.&lt;/p&gt;

&lt;p&gt;Before any AI processing happens, Footwork removes the location data. It sends only an anonymous timeline of movement events to Gemma, Google's open-weight model. Gemma interprets the shape of the journey and creates a structured musical plan. After that plan is checked and validated, a separate renderer turns it into the final piece of music.&lt;/p&gt;

&lt;p&gt;In other words, &lt;strong&gt;the walk itself becomes the prompt&lt;/strong&gt;. You do not have to sit down afterward and explain your experience to an AI. The way you moved already tells the story.&lt;/p&gt;

&lt;p&gt;Footwork is for walkers, runners, and anyone curious about hearing an ordinary journey in a completely different way. It is not meant to score your fitness, recommend a route, or give you another reason to keep checking your phone. It is simply a creative reward for putting the screen away and spending some time outside.&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%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6rqpcn3j274cpng0zk00.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%2F6rqpcn3j274cpng0zk00.png" alt=" " width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try the public app: &lt;a href="https://footwork-m6g0.onrender.com/" rel="noopener noreferrer"&gt;footwork-m6g0.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Fast judge path
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the app and choose &lt;strong&gt;Hear an example&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Press play on the Soundprint.&lt;/li&gt;
&lt;li&gt;Watch the traced path, movement ribbon, and active moment move together.&lt;/li&gt;
&lt;li&gt;Jump to the turn near &lt;code&gt;0:18&lt;/code&gt; and the pause near &lt;code&gt;0:36&lt;/code&gt; to inspect two different movement-to-music ideas.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The example is prepared in the browser so anyone can evaluate the complete interaction without spending generation credits. It is clearly labeled &lt;strong&gt;Example walk&lt;/strong&gt; and is not presented as a live Gemma or ElevenLabs Music call.&lt;/p&gt;

&lt;p&gt;For the real experience, start a walk, keep the tab open, put the phone away, and move for at least 90 seconds of active time. Ending the walk starts the privacy-cleaning, arrangement, and rendering pipeline.&lt;/p&gt;

&lt;p&gt;The source is public on GitHub: &lt;a href="https://github.com/kernelKain/footwork" rel="noopener noreferrer"&gt;github.com/kernelKain/footwork&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Architecture notes: &lt;a href="https://github.com/kernelKain/footwork/blob/main/Docs/architecture.md" rel="noopener noreferrer"&gt;Docs/architecture.md&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    A[Walk captured in browser] --&amp;gt; B[API cleans the trace]
    B --&amp;gt; C[Detect turn, pause, pace change, return]
    C --&amp;gt; D[Remove coordinates and original clock]
    D --&amp;gt; E[Gemma plans the arrangement]
    E --&amp;gt; F[Validate constrained JSON plan]
    F --&amp;gt; G[Eleven Music renders audio]
    G --&amp;gt; H[Soundprint follows one audio clock]
    C --&amp;gt; I[Route Sketch fallback]&lt;/code&gt;&lt;/pre&gt;



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

&lt;p&gt;Footwork is built as a series of small, clearly defined steps. I deliberately avoided sending the entire walk to one large, mysterious AI process. Each part of the pipeline has one job, and the next step only begins when the previous one has produced a valid result.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Record the walk without demanding attention
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table width="100%"&gt;
  &lt;tbody&gt;
&lt;tr&gt;
    &lt;td width="50%"&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%2Fb80cmpaiceilxnrn6m60.png" alt="Footwork screen one" width="800" height="520"&gt;
    &lt;/td&gt;
    &lt;td width="50%"&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%2Fy1ll9117g7ep4xnvvobq.png" alt="Footwork screen two" width="800" height="520"&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The browser records the journey while the user is outside. The recording screen has large controls, pause and resume options, a visible 90-second readiness target, and a hold-to-end button that helps prevent accidental taps.&lt;/p&gt;

&lt;p&gt;Once the walk starts, the user should be able to put the phone away and focus on being outside.&lt;/p&gt;

&lt;p&gt;When the walk ends, the backend cleans the recorded trace and looks for a few meaningful types of movement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sharp turns&lt;/li&gt;
&lt;li&gt;pauses&lt;/li&gt;
&lt;li&gt;changes in pace&lt;/li&gt;
&lt;li&gt;returns near an earlier part of the route&lt;/li&gt;
&lt;/ul&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%2Fxsz09hayszojl1beozva.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%2Fxsz09hayszojl1beozva.png" alt=" " width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These events become the raw material for the music.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Keep the movement, remove the location
&lt;/h3&gt;

&lt;p&gt;Privacy was an important design decision from the beginning. Gemma does not need to know the name of a street or even which country the walk happened in to understand its rhythm.&lt;/p&gt;

&lt;p&gt;Before the data is sent to any model provider, the server removes the coordinates and original timestamps. What remains is an anonymous movement timeline containing event IDs, event types, normalized timing, and the approximate duration of the finished piece.&lt;/p&gt;

&lt;p&gt;This allows the AI to understand how the walk unfolded without receiving the location where it happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Ask Gemma to arrange the journey
&lt;/h3&gt;

&lt;p&gt;I use &lt;a href="https://huggingface.co/google/gemma-4-E2B-it" rel="noopener noreferrer"&gt;&lt;code&gt;google/gemma-4-E2B-it&lt;/code&gt;&lt;/a&gt;, running inside a public Hugging Face ZeroGPU Space, as Footwork's musical director.&lt;/p&gt;

&lt;p&gt;Gemma is not asked to describe the route or invent a story about where the user went. Its job is much more focused: it studies the anonymous movement events and creates a structured instrumental arrangement.&lt;/p&gt;

&lt;p&gt;For example, it can decide that a sharp turn should introduce a new section, a pause should create a musical hold, or a faster stretch should increase the energy. This is where the shape of the walk begins to become the shape of the music.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Check Gemma's plan before generating audio
&lt;/h3&gt;

&lt;p&gt;Gemma must return its arrangement as a small, well-defined JSON plan. Footwork validates that plan before making a music-generation request.&lt;/p&gt;

&lt;p&gt;The validator checks that Gemma only refers to events that actually occurred during the walk. It also rejects unsupported moods, unknown values, and styles outside Footwork's intentionally limited instrumental vocabulary.&lt;/p&gt;

&lt;p&gt;Only a valid arrangement is allowed to continue to Eleven Music. This makes the pipeline more predictable and avoids wasting a music-generation request on a malformed plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Keep the visuals and music on the same clock
&lt;/h3&gt;

&lt;p&gt;Once the Soundprint is ready, the audio player becomes the single source of time for the whole experience.&lt;/p&gt;

&lt;p&gt;The traced path, movement ribbon, selected event, playback progress, and current Soundprint moment all follow the position of the same audio element. There is no separate animation timer running beside it, so the explanation does not slowly drift away from the music.&lt;/p&gt;

&lt;h3&gt;
  
  
  The hardest parts
&lt;/h3&gt;

&lt;p&gt;Some of the most difficult work happened around the details that users should never have to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protecting privacy:&lt;/strong&gt; I needed to preserve the shape and rhythm of a journey while removing the actual place where it happened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handling failure honestly:&lt;/strong&gt; a demo track, a Route Sketch, and a generated Soundprint must always be clearly distinguishable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Making retries safe:&lt;/strong&gt; if a music request times out, Footwork cannot blindly send it again because the original request may already have consumed paid generation quota.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keeping everything synchronized:&lt;/strong&gt; the path, movement events, and explanations must remain aligned when the user plays, pauses, seeks, or leaves the tab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Designing for outdoor use:&lt;/strong&gt; important actions need large touch targets, strong contrast, minimal reading, and reduced-motion alternatives.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a pipeline where each part can be inspected, tested, and replaced independently—while the experience itself remains simple: take a walk, put the phone away, and come back to a piece of music shaped by the journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Footwork's result is more than an audio file. It is a synchronized explanation of how the walk became the piece:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Soundprint&lt;/strong&gt; — the finished playable artifact, pairing the instrumental with the walk's detected moments so movement and music remain readable as one result.&lt;br&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%2Fjji1djejpt3s8myo0il7.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%2Fjji1djejpt3s8myo0il7.png" alt=" " width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Traced path&lt;/strong&gt; — the cleaned route outline preserves the shape of the journey while model providers receive anonymized movement events instead of coordinates.&lt;br&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%2Fsfyvzm11xs13eeqzq9xl.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%2Fsfyvzm11xs13eeqzq9xl.png" alt=" " width="707" height="685"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Movement ribbon&lt;/strong&gt; — a compact, time-ordered view of turns, pauses, pace changes, and returns. During playback, it shows which movement event is shaping the current moment.&lt;br&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%2Fek14upbn8gb64a08m7yp.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%2Fek14upbn8gb64a08m7yp.png" alt=" " width="799" height="243"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Journey pattern&lt;/strong&gt; — the walk's larger structure—where motion changed, stopped, accelerated, or returned becomes the compositional plan rather than incidental activity data.&lt;br&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%2F59mc76g7x322utbvb4cc.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%2F59mc76g7x322utbvb4cc.png" alt=" " width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Synchronized playback&lt;/strong&gt; — the traced path, movement ribbon, and active Soundprint moment all follow one audio clock, keeping the explanation aligned with what the listener hears.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graceful fallback&lt;/strong&gt; — if model planning or music generation fails, the same event timeline still produces a clearly labeled Route Sketch.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Gemma on Hugging Face
&lt;/h2&gt;

&lt;p&gt;Gemma is the decision-making center of Footwork, not a decorative text generator.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; &lt;a href="https://huggingface.co/google/gemma-4-E2B-it" rel="noopener noreferrer"&gt;&lt;code&gt;google/gemma-4-E2B-it&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Space:&lt;/strong&gt; &lt;a href="https://huggingface.co/spaces/kernelKain/footwork" rel="noopener noreferrer"&gt;&lt;code&gt;kernelKain/footwork&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; public Gradio Space on free Hugging Face ZeroGPU (&lt;code&gt;zero-a10g&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Space exposes one narrow &lt;code&gt;/arrange&lt;/code&gt; job. It receives turns, pauses, pace changes, returns, normalized timing, and duration but never coordinates or the original clock. Thinking output is disabled, and the model is asked for one bounded JSON arrangement.&lt;/p&gt;

&lt;p&gt;A checked call returned a validated plan from the pinned model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"google/gemma-4-E2B-it"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"arrangement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warm_cinematic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"instrumental"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cinematic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rising"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"event_refs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"turn-1"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is accepted only when its mood is &lt;code&gt;warm_cinematic&lt;/code&gt;, its style stays within the closed instrumental vocabulary, and every referenced event exists in the submitted timeline. Invalid or unavailable responses remain blocked and fall back to Route Sketch. The Space never invents a successful plan, and only a validated arrangement can be sent to Eleven Music.&lt;/p&gt;

&lt;p&gt;This separation is the architecture: &lt;strong&gt;Gemma interprets the journey, Footwork validates that interpretation, and Eleven Music renders the accepted plan.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Open innovation matters here because the model controls the part of Footwork that makes the project more than a GPS visualizer: &lt;strong&gt;how a journey becomes musical structure.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The planner is inspectable
&lt;/h3&gt;

&lt;p&gt;I can show the exact model, pin its revision, inspect the prompt and response contract, and prove which movement event IDs were accepted. With a closed route-to-music service, the most important creative decision would be hidden behind an endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  The privacy boundary belongs to the project
&lt;/h3&gt;

&lt;p&gt;Footwork decides what the model is allowed to know. Gemma receives &lt;code&gt;turn-1 at normalized time 0.31&lt;/code&gt;; it does not receive a street, a coordinate, or a wall-clock timestamp. Because the planning layer is open and separately hosted, that boundary is visible in the code and testable in isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  The model can move or change without rewriting the product
&lt;/h3&gt;

&lt;p&gt;The planner speaks a small JSON contract. I can tighten the vocabulary, compare a compatible checkpoint, or move inference closer to the device while keeping the capture, validation, renderer, and Soundprint interface intact. Footwork does not run fully offline today, so I am not claiming that benefit yet but the open-weight core keeps that route available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open inference keeps experimentation accessible
&lt;/h3&gt;

&lt;p&gt;The checkpoint runs on a free Hugging Face ZeroGPU Space. That made it possible to test a real, pinned model and expose the planning service publicly without building the experience around paid planning calls. Specialized audio rendering is used only after the open planner has made and passed the decision.&lt;/p&gt;

&lt;p&gt;A closed end-to-end service could produce audio, but it would weaken the two ideas I care about most: an enforceable location boundary and a replaceable, inspectable relationship between movement and music.&lt;/p&gt;

&lt;p&gt;While shaping the submission, I compared the approach with two other outdoor-first community entries: &lt;a href="https://dev.to/subhamduary/wanderink-turn-your-walks-into-stories-with-open-source-ai-228h"&gt;WanderInk by @subhamduary&lt;/a&gt; and &lt;a href="https://dev.to/synthreaper/paper-trail-leaving-the-phone-behind-with-local-open-ai-and-a-printed-walking-card-2o1p"&gt;Paper Trail by @synthreaper&lt;/a&gt;. Both reinforced the value of making the device secondary to the outside experience, while Footwork takes a different path: movement is not context for generated content; it is the score itself.&lt;/p&gt;

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

&lt;p&gt;The agent-assisted development sessions for Footwork are tracked with Entire:&lt;br&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%2Fvgjmz3tj6i4mv3qah0mz.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%2Fvgjmz3tj6i4mv3qah0mz.png" alt=" " width="799" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://entire.io/gh/kernelKain/footwork/sessions" rel="noopener noreferrer"&gt;View the Footwork agent sessions on Entire&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Together with the repository, these sessions provide a reviewable trail of the agent-assisted work behind the build.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — Gemma is the musical director. It converts an anonymous movement timeline into the constrained arrangement that defines the generated piece; without that plan, the studio path does not proceed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best Use of Render&lt;/strong&gt; — Render hosts the public experience&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best Use of ElevenLabs&lt;/strong&gt; — Eleven Music renders the validated instrumental plan into the final audio&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best Use of Entire&lt;/strong&gt; — Provide a reviewable trail of the agent-assisted decisions, implementation steps, and verification work behind the project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best Use of Copilot&lt;/strong&gt; — For debugging purposes after code review by Code Rabbit.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>Arun plans every evening like a research paper. So I built him a planner that is allowed to say it doesn't know.</title>
      <dc:creator>Kshitij</dc:creator>
      <pubDate>Mon, 05 Oct 2026 04:21:23 +0000</pubDate>
      <link>https://dev.to/kernelkain/arun-plans-every-evening-like-a-research-paper-so-i-built-him-a-planner-that-is-allowed-to-say-it-2pod</link>
      <guid>https://dev.to/kernelkain/arun-plans-every-evening-like-a-research-paper-so-i-built-him-a-planner-that-is-allowed-to-say-it-2pod</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;Arun is the friend who plans. I am the friend who says "anything is fine" and then has opinions later.&lt;/p&gt;

&lt;p&gt;He has a system. Ten Google Maps tabs. A screenshot of opening hours that might be from last year. A chatbot paragraph that reads finished. Last month he asked one for drinks in Lisbon, for two, around nine. It came back in seconds: three bars, a note about the lighting, a walking time nobody had measured. One of those bars was shut that weekday. Arun still went, because the answer had sounded sure of itself.&lt;/p&gt;

&lt;p&gt;What got me wasn't that the answer was wrong. It was that there was no way to tell which parts anyone had actually checked.&lt;/p&gt;

&lt;p&gt;So I spent the weekend building him a planner that is allowed to say "I don't know."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happen&lt;/strong&gt; takes one evening, written the way you'd text a friend, and returns one plan where every claim is either sourced or visibly blank.&lt;br&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%2F9mbcznxfnke5ljptuypi.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%2F9mbcznxfnke5ljptuypi.png" alt=" " width="799" height="396"&gt;&lt;/a&gt;&lt;br&gt;
You type something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Drinks in Lisbon on Friday at 9 for two.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then three things happen.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Describe.&lt;/strong&gt; You write the evening in your own words.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review.&lt;/strong&gt; Happen turns it into a short brief: place, date, time, stop order, group size. If something essential is missing or ambiguous, it asks exactly one question. Every field is editable before anything gets searched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan.&lt;/strong&gt; You press &lt;strong&gt;Check live places&lt;/strong&gt;. Happen searches live listings, checks the opening hours for &lt;em&gt;that&lt;/em&gt; day against &lt;em&gt;your&lt;/em&gt; time, and picks at most two stops.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;The landing. One box, three starter evenings, and three promises: live listings, feasibility first, no hidden guesses.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That middle step matters more than I expected, and the screenshot below is the reason I trust it.&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%2F2q2z7iuxlw0351ptzkhd.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%2F2q2z7iuxlw0351ptzkhd.png" alt="Review step before any search: the brief on the left, what happens next on the right" width="800" height="414"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Review, mid-build. The brief on the left had resolved "at 9" to 9:00 AM and "Friday" to Wednesday. Nothing had been searched yet, so the mistake cost nothing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'm showing that screenshot because it caught a real bug, not because it flatters the product. That build read "at 9" as 9 in the morning and "Friday" as the coming Wednesday, then carried both forward without complaint. A planner built on "never claim what you didn't check" was quietly inventing two of its own fields.&lt;/p&gt;

&lt;p&gt;So the parser now treats a bare time as ambiguous and stops to ask, rather than picking one:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What time should the evening start? — &lt;strong&gt;09:00&lt;/strong&gt; or &lt;strong&gt;21:00&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nothing is billed until Arun answers. That one fix is most of the difference between a confident plan and a true one.&lt;/p&gt;

&lt;p&gt;Each stop then shows its time, why it was chosen, the listing details, and one line most planners never show you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Couldn't confirm.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the listing doesn't say whether a place seats two, Happen doesn't guess. If that day's hours are missing, the stop doesn't get the benefit of the doubt. Missing evidence never counts in a place's favour.&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%2F70pi8b71dz3rampxz0jp.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%2F70pi8b71dz3rampxz0jp.png" alt="A finished Lisbon plan: Agave Cocktail Lab at 9pm, open, with one Couldn't confirm line" width="800" height="483"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A finished plan. One stop, open at 9, live listings retrieved at 4:30 AM GMT+1. Couldn't confirm: price, current crowd level, or seating for two.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Note the date in that screenshot is the Wednesday, carried over from the same mid-build parse. The lookups, the hours check, the evidence, and the blank line are all real; only the date was wrong, and only for that one run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;View sources&lt;/strong&gt; opens what was checked, the planned day's hours, the links, and a full audit with the time each fact was retrieved. If Arun wants to know why Ágave made the cut, the answer is a link rather than a paragraph about the ambience.&lt;/p&gt;

&lt;p&gt;And if the plan isn't right, he types the change and chooses &lt;strong&gt;Review this change&lt;/strong&gt;. The current plan stays on screen until he presses &lt;strong&gt;Apply&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  What it deliberately doesn't do
&lt;/h3&gt;

&lt;p&gt;I cut a lot on purpose, and I'd rather say so than let a screenshot imply otherwise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One evening, two stops at most.&lt;/li&gt;
&lt;li&gt;No travel time. A second stop shows its own hours and a directions link, not a made-up arrival.&lt;/li&gt;
&lt;li&gt;No live crowd levels, reservations, or price conversion. Those three are exactly what this Lisbon plan refused to invent.&lt;/li&gt;
&lt;li&gt;No accounts, no saved history, no database. Close the tab and it's gone.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Handing it over
&lt;/h3&gt;

&lt;p&gt;I sent Arun the live URL and asked him to plan the Lisbon evening himself. He typed the prompt, answered the 9 PM question, changed nothing else, and pressed &lt;strong&gt;Check live places&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;He read the Ágave card twice. The thing he pointed at first wasn't the five-star rating. It was the last line.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Okay. So it's open at 9, and it's not pretending to know the price. That's actually useful. I keep getting paragraphs. I wanted the gap."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He got the distinction without me explaining it. &lt;strong&gt;Why it fits&lt;/strong&gt; is what the listings supported: hours covering 9 PM, an official site, a Maps rating. &lt;strong&gt;Couldn't confirm&lt;/strong&gt; is what they didn't: price, how busy it is right now, seating for two. He said he'd still go, and that he'd text the bar about a table, which is the decision Happen is supposed to leave with him.&lt;/p&gt;

&lt;p&gt;What confused him was there being only one stop. He expected a second place, because the landing page says up to two. I gave him the honest version: a second stop only appears if a second place also clears the hours check. Happen won't pad the evening to look complete. His response was that this belongs on the result screen, not just in my explanation. He was right.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://happen-web.onrender.com/" rel="noopener noreferrer"&gt;happen-web.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Type an evening, or tap one of the three starters. &lt;strong&gt;Drinks in Lisbon on Friday at 9 for two&lt;/strong&gt; is the one in the screenshots.&lt;/p&gt;

&lt;p&gt;No login. The first search spends live SerpApi credits; asking for the same evening again within 15 minutes doesn't.&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/kernelKain" rel="noopener noreferrer"&gt;
        kernelKain
      &lt;/a&gt; / &lt;a href="https://github.com/kernelKain/happen" rel="noopener noreferrer"&gt;
        happen
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Find the right place and time for your plans with recommendations backed by reviews, busyness data, and your preferences.
    &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;🌙 Happen&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;em&gt;Evidence-First Evening Planning That Is Allowed To Say It Doesn't Know&lt;/em&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/7a6978c2f5e6620ad5b762538c0c367520266a960932216c31afcbd52138fb8e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f626572666573742d5765656b656e645f4368616c6c656e67655f323032362d6666376135393f7374796c653d666f722d7468652d6261646765" alt="Hacktoberfest 2026"&gt;&lt;/a&gt;
&lt;a href="https://ai.google.dev/gemma" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9c3fa386dc58d3d26607206de77cbf8aa1d0737df3782a5907d6d5d396d7d4b0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6f64656c2d47656d6d615f335f3237304d2d3030653539393f7374796c653d666f722d7468652d6261646765" alt="Gemma"&gt;&lt;/a&gt;
&lt;a href="https://fastapi.tiangolo.com/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b589d81c86d28b4949c3c11e84c7e3f44405eb02e78ca23a053764c1329c036e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4261636b656e642d466173744150492d3030393638383f7374796c653d666f722d7468652d6261646765266c6f676f3d66617374617069" alt="FastAPI"&gt;&lt;/a&gt;
&lt;a href="https://react.dev" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/68999f59219a2d699a8516b03c56084a99abba4daa526e183ead3f4238b001b5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f46726f6e74656e642d52656163745f31395f2532425f54532d3631646166623f7374796c653d666f722d7468652d6261646765266c6f676f3d7265616374" alt="React"&gt;&lt;/a&gt;
&lt;a href="https://serpapi.com/search-api" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0dd514f7d8d61aad90ab92d03b0a9c64500f2d232565dfab6dd5d8ce4ae58fb1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446174612d536572704170692d6666376135393f7374796c653d666f722d7468652d6261646765" alt="SerpApi"&gt;&lt;/a&gt;
&lt;a href="https://github.com/kernelKain/happen/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5cfcf92c18b6f212da3284ba550785d82ac47c107093b4d5813dda5c8faf8696/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d3030653539393f7374796c653d666f722d7468652d6261646765" alt="License: MIT"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/kernelKain/happen/main/docs/press/cover.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FkernelKain%2Fhappen%2Fmain%2Fdocs%2Fpress%2Fcover.png" alt="Happen — turn your evening into a checked plan" width="100%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Built for a Friend:&lt;/strong&gt; Handcrafted for &lt;strong&gt;Arun&lt;/strong&gt;, a friend who plans every evening the same way — ten browser tabs, a screenshot of opening hours that might be from last year, and a chatbot paragraph that reads finished. Last month one of those answers sent him to a bar that was shut that weekday, because the text had sounded sure of itself. So I built him a planner that is allowed to say "I don't know."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Challenge Categories Submitted:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🏆 &lt;strong&gt;Best Use of Gemma&lt;/strong&gt; (Google open-weight model, run locally and gated in code)&lt;/li&gt;
&lt;li&gt;🏆 &lt;strong&gt;Best Use of SerpApi&lt;/strong&gt; (the only place-data provider, with a hard per-plan budget)&lt;/li&gt;
&lt;li&gt;🏆 &lt;strong&gt;Best Use of Entire&lt;/strong&gt; (agent sessions checkpointed throughout the build)&lt;/li&gt;
&lt;li&gt;🏆 &lt;strong&gt;Best Use of Render&lt;/strong&gt; (one-click blueprint, both services defined in &lt;code&gt;render.yaml&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📌 The Problem&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Every evening…&lt;/p&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kernelKain/happen" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;MIT licensed. Python backend (FastAPI), React and TypeScript frontend.&lt;/p&gt;

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

&lt;p&gt;Here's the path one evening takes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Drinks in Lisbon on Friday at 9 for two."
   │
   ▼
Brief  ── place, date, time, stop order, group size
   │       (one question if something essential is missing or ambiguous)
   ▼
SerpApi ── Google Maps listings, official sites, community text
   │       (hard cap: 8 billed requests per plan, 15-minute cache)
   ▼
Python checks ── open on that day at that time?
   │              does the listing support what was asked for?
   │              missing evidence = no credit
   ▼
One plan, at most two stops
   ├── why each stop was chosen
   ├── one "Couldn't confirm" line per stop
   └── sources + audit with retrieval times
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SerpApi is the only data source.&lt;/strong&gt; Every fact on screen traces back to a Google Maps listing, an official website, or community text that SerpApi fetched. The server counts billed requests and refuses the ninth one before it's sent, so a single plan can never cost more than eight. A retry that actually reaches the network stays counted; a cancelled one costs nothing. Identical searches are cached for 15 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python makes the call, not a model.&lt;/strong&gt; Scoring is plain code you can read. It checks feasibility (is this place open at 9 PM that day?) and evidence (does anything actually say this place fits?). Where two sources agree on hours, that's agreement, not a conflict. Where they disagree, you see it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every claim carries its receipt.&lt;/strong&gt; Each fact has a source link and a retrieval time. The "Couldn't confirm" line is generated from what was &lt;em&gt;missing&lt;/em&gt;, not picked from a template. On this plan it's price, crowd, and seating for two, because the listings never said those things.&lt;/p&gt;

&lt;h3&gt;
  
  
  The open model, and the result I didn't expect
&lt;/h3&gt;

&lt;p&gt;This is the part I want to be straight about, because it's the most interesting thing that happened all weekend.&lt;/p&gt;

&lt;p&gt;The original design had &lt;strong&gt;Gemma&lt;/strong&gt; reading the messy text, the community reviews, and the user's prompt, then handing structured evidence to the Python scorer. I picked Gemma 3 270M because it runs on CPU on the same box as the API, with no GPU and no hosted inference. It runs locally through &lt;code&gt;llama.cpp&lt;/code&gt;, from a public quantized build, pinned to an exact revision and verified by SHA-256 before it loads. Temperature 0, fixed seed, so the same input gives the same output.&lt;/p&gt;

&lt;p&gt;Then I did the thing you can really only do with an open model: I measured it myself, on my own task, before letting it anywhere near the product.&lt;/p&gt;

&lt;p&gt;I wrote a held-out set (30 review excerpts, 24 planning prompts) and set gates the model had to clear before any of its output could reach a user:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gate&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Gemma 3 270M&lt;/th&gt;
&lt;th&gt;Gemma 3 1B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valid JSON on review extraction&lt;/td&gt;
&lt;td&gt;95%&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;6.7%&lt;/strong&gt; (2 of 30)&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Right dimension and polarity on reviews&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.4%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;67.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Valid JSON on planning prompts&lt;/td&gt;
&lt;td&gt;95%&lt;/td&gt;
&lt;td&gt;91.7%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Essential planning fields correct&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;td&gt;66.7%&lt;/td&gt;
&lt;td&gt;79.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 270M model mostly couldn't produce valid JSON for my schema. So I tried to fix it the way open weights let you: 100 training examples, no text shared with the held-out set, and a short QLoRA adapter trained on a free Colab T4. It came back at 6.7% parse and 3.3% accuracy — &lt;em&gt;worse&lt;/em&gt; than the untuned model. My rule was that an adapter ships only if it beats baseline by five points without adding invalid output, so it didn't ship. With about eight hours left, I asked my agent whether to pivot the whole project. It said no: the product was already built to refuse when evidence is thin, so a weak model makes it quieter, not wrong.&lt;/p&gt;

&lt;p&gt;The 1B model was a real step up. About 1.3 GB of memory, under a second to load. But it still got review polarity wrong about a third of the time, and for a product whose entire promise is "we only tell you what we checked," a model that's wrong a third of the time about whether a review is positive is worse than no model.&lt;/p&gt;

&lt;p&gt;So it ships &lt;strong&gt;gated off&lt;/strong&gt;, and the gate is code rather than a promise. There's a checked-in quality report (&lt;code&gt;ml/reports/model-quality.json&lt;/code&gt;). At startup the backend hashes the model file and compares it against the hash in that report; model-derived claims are only permitted if the report for &lt;em&gt;that exact file&lt;/em&gt; says it passed. Right now it says it didn't, so a deterministic parser reads the prompt instead, and the health endpoint openly reports &lt;code&gt;model_claims_enabled: false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I wanted to write "Gemma powers Happen." Better headline. But building a tool about not overclaiming and then overclaiming in the write-up felt wrong. What I can say is that the open model shaped the design: it's the reason there's a schema, a validator, a held-out set, and a gate at all. The moment a Gemma build clears those gates, turning it on is a config change.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I worked
&lt;/h3&gt;

&lt;p&gt;I built this over the weekend in Cursor, with Grok 4.7 High as my coding agent. I wrote the product contract first — what it does, what it must never claim, what the request budget is — and checked every change against it.&lt;/p&gt;

&lt;p&gt;I used &lt;strong&gt;Entire&lt;/strong&gt; to checkpoint the agent sessions into the git history, so the reasoning sits next to the code it explains. The public log is here: &lt;a href="https://entire.io/gh/kernelKain/happen/sessions" rel="noopener noreferrer"&gt;entire.io/gh/kernelKain/happen/sessions&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%2F55zffujnr0de74kphg2f.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%2F55zffujnr0de74kphg2f.png" alt="Entire dashboard for Happen: 31 checkpoints, 8 PRs merged, token usage over the weekend" width="799" height="446"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The weekend on Entire. 31 checkpoints across 62 commits, 8 PRs merged, about 5.7 million tokens. The spike is October 4th, when the live planner and the evidence-first UI landed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What the test suite covers right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;466 backend tests, all on mocks and captured fixtures. They never spend a SerpApi credit.&lt;/li&gt;
&lt;li&gt;68 frontend unit tests.&lt;/li&gt;
&lt;li&gt;22 Playwright tests at 1280px and 390px, with automated accessibility checks (axe) on each one.&lt;/li&gt;
&lt;li&gt;A secret scanner across the whole tree, plus a check of the production bundle for keys, hosts, and test fixtures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One bug I'm quietly pleased about catching: a typed follow-up answer was occasionally cleared before it was sent. It failed roughly 1 run in 28, and never when the test ran alone on an idle machine. I only found it by running the suite dozens of times and tallying failures. After the fix it passed 40 runs in a row, and the test now asserts the typed answer actually arrives in the request body, so it can't creep back in.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Because I could check the model instead of trusting it.&lt;/strong&gt; With open weights I downloaded the exact file, pinned its hash, ran it on my own laptop, and scored it against my own task. A closed API would have given me a good demo and a changelog. It wouldn't have let me prove, file by file, that the thing running in production is the thing I tested — and it certainly wouldn't have told me this precisely that the model wasn't ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Because "no" was an option.&lt;/strong&gt; With a hosted model, the model &lt;em&gt;is&lt;/em&gt; the product, and if it isn't good enough you're stuck. Here it's one swappable piece behind a gate. When the 270M failed I tried the 1B by changing a few lines of config. When a larger Gemma clears the bar, it's the same change. No vendor, no new contract, no new API key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Because it costs nothing to run.&lt;/strong&gt; The model shares a CPU with the API. There's no per-token bill, which matters for something I want to hand to a friend and leave running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Because the evidence trail is open too.&lt;/strong&gt; The scoring, the request cap, the quality report, and the gate are all in the repo. If you don't believe a plan, you can read exactly how it was made.&lt;/p&gt;

&lt;p&gt;And because the open path was measurable in a way a hosted one wouldn't be. Gemma ran locally on a CPU, fine-tuned on a free Colab T4, and scored against gates I wrote myself. That's the whole reason the model section above exists at all.&lt;/p&gt;

&lt;p&gt;What did open cost me? Quality, at this size. A large hosted model would probably have cleared my gates on day one. But I'd have had no way to keep checking it, and no way to know when it quietly changed underneath me.&lt;/p&gt;

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

&lt;p&gt;I made the product calls: two stops at most, missing evidence never helps a place, eight requests per plan, and shipping the model gated off rather than pretending otherwise. The agent wrote and tested most of the code against that contract.&lt;/p&gt;

&lt;p&gt;The session I'd point a judge at is the one where the model failed. It walks through running the QLoRA notebook on a free Colab T4, getting 6.7% parse and 3.3% accuracy back, and then asking, with eight hours left, whether the project should be thrown away. The answer was to keep the refusal behaviour, stop tuning, and ship the deploy.&lt;/p&gt;

&lt;p&gt;The full checkpoint trail is on Entire: &lt;a href="https://entire.io/gh/kernelKain/happen/sessions" rel="noopener noreferrer"&gt;entire.io/gh/kernelKain/happen/sessions&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;I'm entering four.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma.&lt;/strong&gt; Gemma is the open-weight model at the centre of this build. I download a pinned public quantized build of Gemma 3 270M, verify its SHA-256 before load, run it locally through &lt;code&gt;llama.cpp&lt;/code&gt; on CPU with no hosted inference, and QLoRA fine-tuned an adapter against it on a free Colab T4. Every one of those steps is only possible because the weights are open.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of SerpApi.&lt;/strong&gt; SerpApi is the only data source in Happen. Google Maps listings, official websites, and community text all come through it, and every fact in a plan links back to the SerpApi result it came from, with a retrieval time. The server enforces a hard budget of eight billed requests per plan — the ninth is refused before it's sent — and caches identical searches for 15 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Entire.&lt;/strong&gt; Agent sessions were checkpointed with Entire throughout the build. The public log is &lt;a href="https://entire.io/gh/kernelKain/happen/sessions" rel="noopener noreferrer"&gt;here&lt;/a&gt;. This weekend: 31 checkpoints, 8 merged PRs, and the reasoning sitting next to the commits it produced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render.&lt;/strong&gt; The live app is &lt;a href="https://happen-web.onrender.com/" rel="noopener noreferrer"&gt;happen-web.onrender.com&lt;/a&gt;. Frontend as a static site, backend as a Python web service in Singapore, both defined in &lt;code&gt;render.yaml&lt;/code&gt; and deployed from &lt;code&gt;main&lt;/code&gt; only after checks pass. The SerpApi key lives in the Render dashboard, never in the repo. The backend build downloads the pinned Gemma file and verifies its hash on Render, with no Hugging Face token and no hosted inference.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built for Arun, who now has one fewer reason to open ten Maps tabs and still end up at the usual place.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
      <category>mlhacks</category>
    </item>
    <item>
      <title>Your most polite customers are the ones you’re about to lose.</title>
      <dc:creator>Kshitij</dc:creator>
      <pubDate>Sun, 30 Aug 2026 17:43:23 +0000</pubDate>
      <link>https://dev.to/kernelkain/your-most-polite-customers-are-the-ones-youre-about-to-lose-3i16</link>
      <guid>https://dev.to/kernelkain/your-most-polite-customers-are-the-ones-youre-about-to-lose-3i16</guid>
      <description>&lt;p&gt;Think of the last time you said an app was “fine.”&lt;/p&gt;

&lt;p&gt;You still have it on your phone. You still open it when you’re tired. You would not recommend it to a friend. You also would not write to support. Support is for a missing order. “Fine” is quieter than that. “Fine” means you order less, then you stop.&lt;/p&gt;

&lt;p&gt;This review is that moment:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“It’s not bad but I’m comparing every food order to getting downstairs 10 floors. The convenience tax is getting loud.”&lt;/p&gt;

&lt;p&gt;— 3 stars&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In plain English: skipping 10 floors of stairs used to be worth the extra fee. Now walking is starting to win. The person still has manners. They even left stars in the middle.&lt;/p&gt;

&lt;p&gt;A normal sentiment tool reads “not bad,” sees 3 stars, and calls it okay. Maybe even positive. Green on the dashboard.&lt;/p&gt;

&lt;p&gt;What happens next is not a complaint. It is fewer orders. Then the app comes off the phone.&lt;/p&gt;

&lt;p&gt;That is the whole product: the review that looks okay and means goodbye.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Resonance is
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Resonance&lt;/strong&gt; reads a sheet of reviews and tells you what people felt, not what they performed. It scores eight emotions. It notices when the words and the feelings disagree. It names the groups hiding in the 3- and 4-star pile. It writes a &lt;strong&gt;Hidden Ask&lt;/strong&gt; — the need nobody filed as a ticket. Then it waits. It will not invent a product roadmap until you say yes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a href="https://graphics-newsletters-usr-simplified.trycloudflare.com" rel="noopener noreferrer"&gt;https://graphics-newsletters-usr-simplified.trycloudflare.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/kernelKain/resonance" rel="noopener noreferrer"&gt;https://github.com/kernelKain/resonance&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch a run:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=QnLD8gl54OU" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=QnLD8gl54OU&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/QnLD8gl54OU" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Two minutes. Then keep reading — the rest is what the clip doesn’t show.&lt;/p&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/kernelKain" rel="noopener noreferrer"&gt;
        kernelKain
      &lt;/a&gt; / &lt;a href="https://github.com/kernelKain/resonance" rel="noopener noreferrer"&gt;
        resonance
      &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;Resonance&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Customer emotion archaeology powered by Plutchik's Wheel&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://graphics-newsletters-usr-simplified.trycloudflare.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/bd57870e4e99ee65279934914c70b8e25412605e595d0afc63c3f7206d181e1a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6976655f64656d6f2d747279636c6f7564666c6172652d303043374237" alt="Live demo"&gt;&lt;/a&gt;
&lt;a href="https://github.com/kernelKain/resonance/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c2c54bf8409d6c3e378ad8f666d1a2dbe508be1980c7c54e60dc79ad9a210065/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d6379616e2e737667" alt="License: MIT"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Resonance does not classify reviews as positive, negative, or neutral. It scores each review on Plutchik's eight emotions, flags cognitive dissonance, maps an unmet Maslow need, clusters the emotion vectors in a TrueForge sandbox, names psychological archetypes, writes Hidden Asks — then &lt;strong&gt;pauses for a human&lt;/strong&gt; before any product-roadmap recommendation is emitted.&lt;/p&gt;
&lt;p&gt;Built for &lt;a href="https://wemakedevs.org/" rel="nofollow noopener noreferrer"&gt;The Agent Harness Hackathon&lt;/a&gt; on the TrueForge harness (filesystem MCP, Exa web search, Daytona sandbox, &lt;code&gt;ask_user_question&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://graphics-newsletters-usr-simplified.trycloudflare.com" rel="nofollow noopener noreferrer"&gt;https://graphics-newsletters-usr-simplified.trycloudflare.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Always-on Oracle Always Free ARM VM in Mumbai. Nothing runs on a laptop. The URL is a Cloudflare Quick Tunnel (&lt;code&gt;*.trycloudflare.com&lt;/code&gt;); it can change if that tunnel process restarts.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Screenshots&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Zomato demo run (Plutchik profile → HITL approval → roadmap).&lt;/p&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/kernelKain/resonance/demo_data/Results/Zomato/live-analysis.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FkernelKain%2Fresonance%2FHEAD%2Fdemo_data%2FResults%2FZomato%2Flive-analysis.png" alt="Zomato — live analysis workbench"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/kernelKain/resonance/demo_data/Results/Zomato/emotion-profile.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FkernelKain%2Fresonance%2FHEAD%2Fdemo_data%2FResults%2FZomato%2Femotion-profile.png" alt="Eight-dimension Plutchik emotion profile" width="48%"&gt;&lt;/a&gt;
  &amp;nbsp;
  &lt;a rel="noopener noreferrer" href="https://github.com/kernelKain/resonance/demo_data/Results/Zomato/segments.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FkernelKain%2Fresonance%2FHEAD%2Fdemo_data%2FResults%2FZomato%2Fsegments.png" alt="Psychological archetype segment card" width="48%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/kernelKain/resonance/demo_data/Results/Zomato/approval.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FkernelKain%2Fresonance%2FHEAD%2Fdemo_data%2FResults%2FZomato%2Fapproval.png" alt="Human-in-the-loop approval gate before recommendations" width="48%"&gt;&lt;/a&gt;
  &amp;nbsp;
  &lt;a rel="noopener noreferrer" href="https://github.com/kernelKain/resonance/demo_data/Results/Zomato/recommendations.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FkernelKain%2Fresonance%2FHEAD%2Fdemo_data%2FResults%2FZomato%2Frecommendations.png" alt="Roadmap recommendations after approval" width="48%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/kernelKain/resonance/demo_data/Results/Zomato/unspoken-needs.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FkernelKain%2Fresonance%2FHEAD%2Fdemo_data%2FResults%2FZomato%2Funspoken-needs.png" alt="Hidden Asks — unmet needs no review filed as a ticket" width="48%"&gt;&lt;/a&gt;
  &amp;nbsp;
  &lt;a rel="noopener noreferrer" href="https://github.com/kernelKain/resonance/demo_data/Results/Zomato/red-flags.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FkernelKain%2Fresonance%2FHEAD%2Fdemo_data%2FResults%2FZomato%2Fred-flags.png" alt="Cognitive dissonance red flags on individual reviews" width="48%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/kernelKain/resonance/demo_data/Results/Zomato/executive-summary.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FkernelKain%2Fresonance%2FHEAD%2Fdemo_data%2FResults%2FZomato%2Fexecutive-summary.png" alt="Executive summary with PDF export" width="48%"&gt;&lt;/a&gt;
  &amp;nbsp;
  &lt;a rel="noopener noreferrer" href="https://github.com/kernelKain/resonance/demo_data/Results/Zomato/light-mode.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FkernelKain%2Fresonance%2FHEAD%2Fdemo_data%2FResults%2FZomato%2Flight-mode.png" alt="Light-mode workbench" width="48%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Upload a CSV → watch the Plutchik wheel fill in real time → approve the analysis → export a dark-theme PDF.&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Sample analysis&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;table&gt;

&lt;thead&gt;

&lt;tr&gt;

&lt;th&gt;Product&lt;/th&gt;

&lt;th&gt;Dataset&lt;/th&gt;

&lt;th&gt;Report&lt;/th&gt;

&lt;/tr&gt;

&lt;/thead&gt;

&lt;/table&gt;&lt;/div&gt;…&lt;p&gt;&lt;/p&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kernelKain/resonance" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Built for &lt;a href="https://www.wemakedevs.org/hackathons/trueforge" rel="noopener noreferrer"&gt;The Agent Harness Hackathon&lt;/a&gt; (WeMakeDevs × TrueForge × Qodo). The live URL is a Cloudflare tunnel on a small always-on VM. If that process restarts, the hostname can change. The repo and the video stay.&lt;/p&gt;

&lt;p&gt;Drop a CSV. Watch the wheel fill. Approve or decline. Download a PDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  The job I gave the agent
&lt;/h2&gt;

&lt;p&gt;A product manager already has a spreadsheet. Their dashboard says “mostly positive.” They need the people hiding in the polite ratings.&lt;/p&gt;

&lt;p&gt;The job, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the file. Don’t make me paste reviews into a chat.&lt;/li&gt;
&lt;li&gt;Learn what the product is, so “I love it” on food is not “I love it” on a bank.&lt;/li&gt;
&lt;li&gt;Score every row: eight emotions, whether the words fight the feeling, one unmet need.&lt;/li&gt;
&lt;li&gt;Group similar emotion patterns with real math — not a guess.&lt;/li&gt;
&lt;li&gt;Name the groups. Write the Hidden Asks.&lt;/li&gt;
&lt;li&gt;Ask me before any “you should build X.”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The eight emotions are Plutchik’s, not happy / sad / meh. Politeness is not joy.&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%2Fe59wwb754g6m5wvcj2jc.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%2Fe59wwb754g6m5wvcj2jc.png" alt="Eight-emotion wheel for the Zomato run. Sadness and anger sit high. Joy is low." width="782" height="868"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the job: a shape of feeling, not a thumbs-up.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TrueForge: the harness in the middle
&lt;/h2&gt;

&lt;p&gt;A chatbot answers. An agent has to reach a file, run code somewhere safe, and stop before it does something you can’t undo. I didn’t want to spend the week building that machinery. TrueForge is the layer between the model and everything it touches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You (browser)  →  workbench  →  TrueForge agent
                                   ├ filesystem (the CSV)
                                   ├ web research (what is this product?)
                                   └ sandbox (the clustering script)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What the harness handled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Files&lt;/strong&gt; — the agent reads the upload by name and writes results. It doesn’t get the rest of the disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A specialist&lt;/strong&gt; — one helper looks up the product. Scoring and the wait stay on the main agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A sandbox&lt;/strong&gt; — grouping runs in a sealed machine, not on my laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A pause&lt;/strong&gt; — after Hidden Asks, TrueForge stops and asks. Roadmap items stay empty until &lt;strong&gt;Approve&lt;/strong&gt;. &lt;strong&gt;Decline&lt;/strong&gt; ends there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wrote the screen, the parser, and the PDF. The harness wrote the loop, the tools, the sandbox, and the wait.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TrueForge paused on &lt;code&gt;ask_user_question&lt;/code&gt; — waiting for Approved or Decline.&lt;/p&gt;
&lt;/blockquote&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%2F8dzmkvrqgwc14u9js55q.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%2F8dzmkvrqgwc14u9js55q.png" alt="Live analysis after a finished run. The activity log shows TrueForge paused, waiting for Approve or Decline." width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The wait is not a fake button. The session actually stops.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stage 1 — the model vanished.&lt;/strong&gt; The first provider ran out of quota. I moved the agent to OpenRouter: one model first, a second if the first turn fails. A run stays on one model until you approve. Unsexy. Why the demo still works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 2 — the sealed room had no door.&lt;/strong&gt; I told it to run the clustering script on the scored file. The sandbox had never seen that file. I had to copy the script &lt;em&gt;and&lt;/em&gt; the data in. After that, it was just a tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 3 — the answer got cut in half.&lt;/strong&gt; Long replies died mid-stream. The UI said done. The wheel was empty. I stopped asking the model to “be complete” and started only drawing what I could actually read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 4 — the first model got tired.&lt;/strong&gt; Same job, second model, more room. Not a second opinion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 5 — the small computer had opinions.&lt;/strong&gt; The app had to listen for the real visitor IP behind the tunnel. The harness on that machine had to listen on all network interfaces or Docker couldn’t talk to it. The public URL is a tunnel. The only open door is SSH.&lt;/p&gt;

&lt;p&gt;That’s the messy middle. That’s also why a stranger can click the link.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qodo from the first PR
&lt;/h2&gt;

&lt;p&gt;Every real change went through a pull request. Qodo read it before it landed. That was the way the repo grew, not a polish pass at the end.&lt;/p&gt;

&lt;p&gt;Example: &lt;a href="https://github.com/kernelKain/resonance/pull/4" rel="noopener noreferrer"&gt;PR #4 — the approval step&lt;/a&gt;. The dangerous part of this product is emitting a roadmap. Qodo caught a High bug: if the pause data failed to parse, &lt;strong&gt;Approve would fake a local replay, mark the run done, and never send the answer back to TrueForge.&lt;/strong&gt; The wait would look real. The harness would never hear you.&lt;/p&gt;

&lt;p&gt;I stopped inventing a fake replay id. Approve only continues when the pause is a real TrueForge interrupt.&lt;/p&gt;

&lt;p&gt;I don’t merge because the bot is green. I merge when the note is fixed, or the thread says why we skipped it.&lt;/p&gt;

&lt;p&gt;All merged work: &lt;a href="https://github.com/kernelKain/resonance/pulls?q=is%3Apr+is%3Amerged" rel="noopener noreferrer"&gt;the PR list&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Zomato run
&lt;/h2&gt;

&lt;p&gt;I uploaded &lt;code&gt;zomato_reviews.csv&lt;/code&gt;. Fifty reviews. No extra chat window — the file is the input.&lt;/p&gt;

&lt;p&gt;First, the polite ones that don’t mean polite.&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%2Fb5wdull24z8gsfz3l8s0.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%2Fb5wdull24z8gsfz3l8s0.png" alt="Review cards. One says “not bad” and is tagged praise masking pain, 3 stars." width="725" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Same quote as the opening. Now you can see the tag.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then the group that still opens the app.&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%2F5o0x2u6v9672xdskynol.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%2F5o0x2u6v9672xdskynol.png" alt="Segment card: Fee-Fatigued Loyalists. 22 reviews. 44% of the set. Esteem." width="723" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;They stay because discovery still works. One bad bill and they don’t.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then the need that never became a ticket.&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%2F448wx4127i2z2sjua47x.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%2F448wx4127i2z2sjua47x.png" alt="Hidden Ask card: “A path back to a real human.” Belonging. High." width="720" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not “make the bot nicer.” A person who can undo a wrong charge.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then it waits. The roadmap side is blank on purpose.&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%2Fs7e4tijnqbcchbqu15cv.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%2Fs7e4tijnqbcchbqu15cv.png" alt="Approve recommendations modal, Hidden Asks listed, Decline and Approve buttons." width="657" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Approve continues. Decline stops here. I tried both.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Only after Approve:&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%2Fwwntffopq8b3mfj6s5wk.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%2Fwwntffopq8b3mfj6s5wk.png" alt="Roadmap card: one-tap “talk to a person” after two failed chat loops." width="720" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Same ask, now as something you could ship. That sentence did not exist before a human said yes.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Go try it
&lt;/h2&gt;

&lt;p&gt;Open the live link at the top, or clone the repo, and hit &lt;strong&gt;Load demo dataset&lt;/strong&gt;. Approve once. Decline once. The interesting screen is the one where the roadmap is still empty.&lt;/p&gt;

</description>
      <category>hackathon</category>
      <category>ai</category>
      <category>trueforge</category>
      <category>opensource</category>
    </item>
    <item>
      <title>PawSpective: See the World Closer to How Your Dog Sees It</title>
      <dc:creator>Kshitij</dc:creator>
      <pubDate>Mon, 17 Aug 2026 04:44:53 +0000</pubDate>
      <link>https://dev.to/kernelkain/pawspective-see-the-world-closer-to-how-your-dog-sees-it-2k4g</link>
      <guid>https://dev.to/kernelkain/pawspective-see-the-world-closer-to-how-your-dog-sees-it-2k4g</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-08-13"&gt;Weekend Challenge: Dog Days Edition&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Have you ever bought a bright red toy that looked impossible to miss only to watch your dog struggle to find it on green grass?&lt;/p&gt;

&lt;p&gt;Humans and dogs do not experience color in the same way. That inspired me to build &lt;strong&gt;PawSpective&lt;/strong&gt;, an application that combines a canine-vision approximation, AI-assisted scene understanding, deterministic visibility calculations, and playful narrated videos.&lt;/p&gt;

&lt;p&gt;The central idea is one connected experience:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Capture a scene → explore a canine-vision approximation → discover which objects remain visible → turn the moment into a fictional story.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I also wanted PawSpective to be entertaining without pretending it can read a dog’s mind. It does not claim to determine exact canine vision, gaze, thoughts, feelings, smell, or intent.&lt;/p&gt;

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

&lt;p&gt;PawSpective helps dog owners explore how objects in an everyday scene may appear under a canine-inspired color transformation.&lt;/p&gt;

&lt;p&gt;A user can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a lightweight dog profile.&lt;/li&gt;
&lt;li&gt;Open the &lt;strong&gt;Live Dog Lens&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;compare Human View with a canine-vision approximation.&lt;/li&gt;
&lt;li&gt;Record or upload a short video.&lt;/li&gt;
&lt;li&gt;Let Google Gemini identify visible objects and scene events.&lt;/li&gt;
&lt;li&gt;Review, rename, or remove incorrect AI detections.&lt;/li&gt;
&lt;li&gt;Calculate the relative dog-visible contrast of an object.&lt;/li&gt;
&lt;li&gt;Explore possible attention cues through the &lt;strong&gt;Curiosity Map&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Compare alternative toy colors in the &lt;strong&gt;Toy Color Lab&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Generate and download a fictional narrated &lt;strong&gt;Story Reel&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Live Dog Lens
&lt;/h3&gt;

&lt;p&gt;The Live Dog Lens uses a browser-based WebGL transformation to provide an immediate Human/Dog Vision comparison.&lt;/p&gt;

&lt;p&gt;The transformation preserves more blue and yellow differentiation while reducing red and green differentiation. A comparison slider makes the effect easy to understand, and a dog-height guide helps users frame the scene from a lower point of view.&lt;/p&gt;

&lt;p&gt;This is deliberately described as a &lt;strong&gt;canine-vision approximation&lt;/strong&gt;, not an exact reconstruction of what a particular dog sees.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI scene analysis
&lt;/h3&gt;

&lt;p&gt;After a user uploads a 5–15 second video, PawSpective sends a normalized, silent version of the clip to Google Gemini.&lt;/p&gt;

&lt;p&gt;Gemini returns structured scene evidence, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visible object labels&lt;/li&gt;
&lt;li&gt;Approximate bounding boxes&lt;/li&gt;
&lt;li&gt;Timestamps&lt;/li&gt;
&lt;li&gt;Object categories&lt;/li&gt;
&lt;li&gt;Confidence values&lt;/li&gt;
&lt;li&gt;Visible evidence&lt;/li&gt;
&lt;li&gt;Coarse motion levels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The response must pass a strict Pydantic and JSON Schema contract before the application accepts it.&lt;/p&gt;

&lt;p&gt;Users can then remove incorrect events, rename objects, and select which object should be analyzed. These corrections become the source of truth for visibility calculations and Story Reel generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visibility Lab
&lt;/h3&gt;

&lt;p&gt;The Visibility Lab measures how strongly a selected object differs from its nearby background.&lt;/p&gt;

&lt;p&gt;The backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seeks to the event timestamp.&lt;/li&gt;
&lt;li&gt;Samples pixels inside the object’s corrected bounding box.&lt;/li&gt;
&lt;li&gt;Samples a surrounding background region.&lt;/li&gt;
&lt;li&gt;Applies the same canine color transformation used by the frontend.&lt;/li&gt;
&lt;li&gt;Converts colors into CIE Lab.&lt;/li&gt;
&lt;li&gt;Measures foreground/background color and luminance separation.&lt;/li&gt;
&lt;li&gt;Produces a relative dog-visible contrast score.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is presented as a product score and qualitative visibility band—not as a scientific probability.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dog-visible contrast: High&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The blue toy remains visually distinct from the surrounding grass after the canine-vision transformation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Curiosity Map
&lt;/h3&gt;

&lt;p&gt;The Curiosity Map highlights objects that may visually stand out in the scene.&lt;/p&gt;

&lt;p&gt;It combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI-inferred motion&lt;/li&gt;
&lt;li&gt;Measured dog-visible contrast&lt;/li&gt;
&lt;li&gt;Apparent object size&lt;/li&gt;
&lt;li&gt;A small optional profile-relevance bonus&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every result explains why it appeared. PawSpective calls these &lt;strong&gt;possible attention cues&lt;/strong&gt;, because the application does not track a dog’s gaze or know what the dog is actually paying attention to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Toy Color Lab
&lt;/h3&gt;

&lt;p&gt;Toy Color Lab compares six screen colors against the measured background around a selected object.&lt;/p&gt;

&lt;p&gt;It keeps the surrounding background unchanged, simulates alternative colors for the object, applies the canine transformation, and ranks the colors by approximate dog-visible contrast.&lt;/p&gt;

&lt;p&gt;The preview is illustrative—it does not claim that a physical toy will look exactly the same or guarantee a dog’s response—but it makes the red-versus-blue-on-grass problem immediately understandable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Story Reel
&lt;/h3&gt;

&lt;p&gt;The final step turns the reviewed scene into an 8–10 second vertical video.&lt;/p&gt;

&lt;p&gt;Google Gemini uses only the user-reviewed scene timeline to generate grounded story and animation direction. ElevenLabs creates the fictional dog narration, and FFmpeg combines the video, narration, captions, overlays, music, watermark, and disclaimer into a downloadable 9:16 MP4.&lt;/p&gt;

&lt;p&gt;The result remains clearly labeled &lt;strong&gt;Just for fun&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;The application is available at:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://pawspective.onrender.com/" rel="noopener noreferrer"&gt;https://pawspective.onrender.com/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Demo note:&lt;/strong&gt; [My Gemini API daily usage limit ran out, so I couldn’t publish the complete demo.]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The interface and deployed application can still be explored through the link above. The complete demonstration will be added to the repository once the provider usage limit is available again.&lt;/p&gt;

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

&lt;p&gt;The complete source code is available on GitHub:&lt;/p&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/kernelKain" rel="noopener noreferrer"&gt;
        kernelKain
      &lt;/a&gt; / &lt;a href="https://github.com/kernelKain/pawSpective" rel="noopener noreferrer"&gt;
        pawSpective
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      See the world closer to how your dog sees it. PawSpective combines canine-vision simulation, AI-powered object visibility analysis, curiosity mapping, and playful narrated story reels.
    &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;PawSpective&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;See the world closer to how your dog sees it. PawSpective combines a
canine-vision approximation, reviewed AI scene analysis, deterministic
visibility scoring, color comparison, curiosity mapping, and downloadable
fictional Story Reels.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Opens a live Human/Dog Vision comparison or accepts a 5-15 second video.&lt;/li&gt;
&lt;li&gt;Detects visible objects with Gemini and lets the user correct the result.&lt;/li&gt;
&lt;li&gt;Measures foreground/background contrast with OpenCV and CIE Lab color.&lt;/li&gt;
&lt;li&gt;Shows possible attention cues in a timestamp-aligned Curiosity Map.&lt;/li&gt;
&lt;li&gt;Compares six screen colors in Toy Color Lab.&lt;/li&gt;
&lt;li&gt;Creates an 8-10 second animated dog-height POV reel with fictional narration.&lt;/li&gt;
&lt;li&gt;Supports a SHA-256-bound controlled demo for offline rehearsals.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PawSpective labels deterministic calculations as &lt;strong&gt;Research-grounded&lt;/strong&gt;
model interpretation as &lt;strong&gt;AI-inferred&lt;/strong&gt;, and fictional output as
&lt;strong&gt;Just for fun&lt;/strong&gt;. It does not claim exact canine vision, gaze, thoughts,
emotions, smell, intent, or behavioral diagnosis. See the
&lt;a href="https://github.com/kernelKain/pawSpective/docs/AI_DISCLOSURE.md" rel="noopener noreferrer"&gt;AI disclosure&lt;/a&gt; for data flow, limitations, and provider
details.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kernelKain/pawSpective" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Repository: &lt;a href="https://github.com/kernelKain/pawSpective" rel="noopener noreferrer"&gt;https://github.com/kernelKain/pawSpective&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository also contains the product contract, AI disclosure, deployment guide, exported schemas, automated tests, Docker configuration, and release checklist.&lt;/p&gt;

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

&lt;p&gt;PawSpective uses a Next.js frontend and a Python media-processing backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;The frontend uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 16&lt;/li&gt;
&lt;li&gt;React 19&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;WebGL for the live color transformation&lt;/li&gt;
&lt;li&gt;Canvas and video APIs for previews and overlays&lt;/li&gt;
&lt;li&gt;Vitest and Testing Library&lt;/li&gt;
&lt;li&gt;Playwright for end-to-end smoke testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser handles the live Dog Lens, comparison slider, dog profile, recording and upload flow, object-correction interface, visibility results, Toy Color Lab, Curiosity Map, job polling, and Story Reel download.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;The backend uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FastAPI&lt;/li&gt;
&lt;li&gt;Pydantic&lt;/li&gt;
&lt;li&gt;Google Gemini&lt;/li&gt;
&lt;li&gt;OpenCV&lt;/li&gt;
&lt;li&gt;NumPy&lt;/li&gt;
&lt;li&gt;ElevenLabs&lt;/li&gt;
&lt;li&gt;FFmpeg and FFprobe&lt;/li&gt;
&lt;li&gt;SQLite&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FastAPI validates uploaded media, normalizes videos, coordinates AI requests, performs deterministic calculations, and manages background Story Reel jobs.&lt;/p&gt;

&lt;p&gt;SQLite stores job metadata and progress, while generated media is stored on a writable backend volume. Story rendering runs through a bounded background worker so the frontend can submit a job, poll its progress, and download the completed reel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Google Gemini
&lt;/h3&gt;

&lt;p&gt;Gemini is used for three related tasks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scene analysis&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Gemini examines the short clip and returns visible objects, approximate bounding boxes, timestamps, motion levels, and supporting visual evidence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Grounded story generation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Gemini receives the reviewed event timeline instead of being allowed to invent details directly from the raw video.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Animation direction&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Depending on the configured model and access, Gemini Omni or Veo can create a canine-vision-inspired artistic edit using the source clip or reference frames.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of my most important engineering decisions was requiring structured model output. Gemini responses are validated against strict schemas, and malformed coordinates, unsupported fields, impossible timestamps, or ungrounded results are rejected.&lt;/p&gt;

&lt;h3&gt;
  
  
  ElevenLabs
&lt;/h3&gt;

&lt;p&gt;ElevenLabs generates the complete fictional dog narration for the Story Reel.&lt;/p&gt;

&lt;p&gt;Only the finalized narration text is sent to ElevenLabs—the original video is not. The selected voice is intentionally described as a &lt;strong&gt;fictional dog voice&lt;/strong&gt;, rather than an attempt to recreate a real animal’s voice or internal thoughts.&lt;/p&gt;

&lt;p&gt;The application also supports narration failure handling and cached demo output so a temporary voice-service problem does not break the entire user journey.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deterministic computer vision
&lt;/h3&gt;

&lt;p&gt;I did not want an AI confidence value to become a fake scientific visibility score.&lt;/p&gt;

&lt;p&gt;Instead, the Visibility Lab and Toy Color Lab use deterministic OpenCV and NumPy calculations. They sample pixels from the video, apply the documented color transformation, convert the colors to CIE Lab, and measure relative foreground/background contrast.&lt;/p&gt;

&lt;p&gt;This creates a clear boundary between what the model inferred and what the application calculated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Video composition
&lt;/h3&gt;

&lt;p&gt;FFmpeg performs the final media work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Video normalization&lt;/li&gt;
&lt;li&gt;Portrait 9:16 composition&lt;/li&gt;
&lt;li&gt;Captions&lt;/li&gt;
&lt;li&gt;Narration synchronization&lt;/li&gt;
&lt;li&gt;Curiosity overlays&lt;/li&gt;
&lt;li&gt;Visibility result cards&lt;/li&gt;
&lt;li&gt;Music&lt;/li&gt;
&lt;li&gt;Watermarking&lt;/li&gt;
&lt;li&gt;Final MP4 encoding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using a fixed template made the rendering pipeline more reliable and kept the project focused on one polished experience instead of becoming a general-purpose video editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reliability and responsible AI
&lt;/h3&gt;

&lt;p&gt;PawSpective separates its output into three categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Research-grounded:&lt;/strong&gt; deterministic transformations and calculations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-inferred:&lt;/strong&gt; visible-object and motion interpretation from Gemini&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Just for fun:&lt;/strong&gt; fictional narration and story framing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application includes recovery paths for camera denial, unsupported browsers, malformed AI output, dark footage, empty detections, narration failures, rendering errors, portrait and landscape uploads, and missing profile photos.&lt;/p&gt;

&lt;p&gt;It also supports a controlled rehearsal mode. Cached analysis and media are bound to one exact video using a SHA-256 fingerprint, preventing saved bounding boxes from being incorrectly applied to an unrelated upload.&lt;/p&gt;

&lt;p&gt;This separation became one of the most important lessons from the project: an AI product can be playful while still explaining where every result came from.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Best Use of Google AI
&lt;/h3&gt;

&lt;p&gt;Google Gemini powers PawSpective’s structured scene analysis, grounded story generation, and optional video-animation workflow.&lt;/p&gt;

&lt;p&gt;Gemini is not used as an unrestricted narrator. Its output is schema-validated, reviewed by the user, and checked against visible scene evidence before it enters later stages of the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Use of ElevenLabs
&lt;/h3&gt;

&lt;p&gt;ElevenLabs converts the grounded fictional script into narration for the downloadable Story Reel.&lt;/p&gt;

&lt;p&gt;Voice generation is integrated into a complete media pipeline rather than presented as an isolated text-to-speech example. The narration is synchronized with captions, scene events, overlays, music, and the final vertical video.&lt;/p&gt;

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

&lt;p&gt;The hardest part was not adding more AI. It was deciding which parts should not depend on AI.&lt;/p&gt;

&lt;p&gt;Gemini is useful for interpreting a scene, but object visibility is better represented by deterministic calculations over measured pixels. Storytelling can be imaginative, but it must remain grounded in events the user has reviewed.&lt;/p&gt;

&lt;p&gt;That led to the design principle behind PawSpective:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use AI for interpretation and creativity, deterministic code for measurable calculations, and clear labels wherever uncertainty remains.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;PawSpective started as a fun question about how dogs experience color. It became an experiment in building an AI-powered product that combines delight, practical insight, transparency, and responsible storytelling.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
    <item>
      <title>I Built a Tool to Detect Delayed Access Revocation</title>
      <dc:creator>Kshitij</dc:creator>
      <pubDate>Sun, 09 Aug 2026 20:16:36 +0000</pubDate>
      <link>https://dev.to/kernelkain/i-built-a-tool-to-detect-delayed-access-revocation-427d</link>
      <guid>https://dev.to/kernelkain/i-built-a-tool-to-detect-delayed-access-revocation-427d</guid>
      <description>&lt;p&gt;This weekend, I built &lt;strong&gt;TimeTrap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It started with a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if access is revoked, but something that depends on it stays active?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That small delay can become a real security problem.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://web-2b33.prg1.zerops.app" rel="noopener noreferrer"&gt;Try TimeTrap live&lt;/a&gt;&lt;br&gt;&lt;br&gt;
💻 &lt;a href="https://github.com/kernelKain/timetrap" rel="noopener noreferrer"&gt;View the source code&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Imagine a user cancels a premium subscription at minute 10.&lt;/p&gt;

&lt;p&gt;The subscription is revoked immediately, but a cached permission remains active until minute 60. The system allows a five-minute grace period.&lt;/p&gt;

&lt;p&gt;So the timeline looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Minute 10&lt;/td&gt;
&lt;td&gt;Subscription is revoked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minute 15&lt;/td&gt;
&lt;td&gt;Allowed grace period ends&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minute 60&lt;/td&gt;
&lt;td&gt;Cached access finally expires&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The user keeps premium access for 50 minutes after cancellation. Of those, &lt;strong&gt;45 minutes violate the rule&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each part of the system may look correct by itself. The subscription was revoked, and the cache eventually expired. The problem is the gap between those two events.&lt;/p&gt;

&lt;p&gt;That is what TimeTrap finds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What TimeTrap does
&lt;/h2&gt;

&lt;p&gt;You describe the objects in your authorization flow—subscriptions, caches, sessions, or tokens—and add events such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;issue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;refresh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;revoke&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;expire&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you choose a rule, such as “cached access must end within five minutes of subscription revocation.”&lt;/p&gt;

&lt;p&gt;TimeTrap checks the timeline and shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether the scenario is safe;&lt;/li&gt;
&lt;li&gt;the exact time the violation begins and ends;&lt;/li&gt;
&lt;li&gt;how long stale access remains active;&lt;/li&gt;
&lt;li&gt;the events that caused the problem;&lt;/li&gt;
&lt;li&gt;a suggested correction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the example above, TimeTrap reports the violation as &lt;code&gt;[15, 60)&lt;/code&gt; and suggests revoking the cached permission when the subscription is revoked.&lt;/p&gt;

&lt;p&gt;You can see the complete flow here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://web-2b33.prg1.zerops.app/results/d28ecf3d-e28a-4538-a74c-d88d791d57fe" rel="noopener noreferrer"&gt;Unsafe result&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web-2b33.prg1.zerops.app/results/1998c072-7095-4d5f-a45f-7a520a3d62c8" rel="noopener noreferrer"&gt;Corrected result&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The analyzer is written in Go and is fully deterministic. The same input always produces the same result.&lt;/p&gt;

&lt;p&gt;Instead of checking every minute, it checks only the moments where something can change: an issue, refresh, revocation, expiry, or rule deadline. It then joins failed periods into one clear violation interval.&lt;/p&gt;

&lt;p&gt;The rest of the application uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React and TypeScript for the interface;&lt;/li&gt;
&lt;li&gt;Go for the API and analyzer;&lt;/li&gt;
&lt;li&gt;PostgreSQL for saved scenarios and results;&lt;/li&gt;
&lt;li&gt;Zerops for deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Saved results use their own URLs, so they can be refreshed or shared. Applying a fix creates a corrected copy instead of replacing the original unsafe result.&lt;/p&gt;

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

&lt;p&gt;Authorization is not only about &lt;strong&gt;who can access something&lt;/strong&gt;. It is also about &lt;strong&gt;when that access should stop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;TimeTrap is currently a design-time tool. It checks the scenario you provide; it does not inspect or change a live production system. The model is intentionally small and uses integer-minute timing, but it makes this easy-to-miss problem visible.&lt;/p&gt;

&lt;p&gt;OpenAI Codex helped me with planning, implementation, testing, documentation, and deployment. I still reviewed the code and verified the behavior through tests, builds, API checks, and browser testing.&lt;/p&gt;

&lt;p&gt;I built TimeTrap for the WeMakeDevs challenge and deployed it on Zerops.&lt;/p&gt;

&lt;p&gt;If this kind of authorization problem sounds familiar, try the demo and let me know what you think.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://web-2b33.prg1.zerops.app" rel="noopener noreferrer"&gt;Open TimeTrap&lt;/a&gt;&lt;br&gt;&lt;br&gt;
⭐ &lt;a href="https://github.com/kernelKain/timetrap" rel="noopener noreferrer"&gt;Explore the GitHub repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>security</category>
      <category>react</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
