<?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: Aditya</title>
    <description>The latest articles on DEV Community by Aditya (@adityakumarpuri).</description>
    <link>https://dev.to/adityakumarpuri</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%2F3288446%2Ff341a095-af24-4414-a246-d378d766fc46.jpg</url>
      <title>DEV Community: Aditya</title>
      <link>https://dev.to/adityakumarpuri</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adityakumarpuri"/>
    <language>en</language>
    <item>
      <title>Dead Frequency: a ghost in the machine, with a fuse box</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Sat, 31 Oct 2026 19:29:40 +0000</pubDate>
      <link>https://dev.to/adityakumarpuri/dead-frequency-a-ghost-in-the-machine-with-a-fuse-box-2pog</link>
      <guid>https://dev.to/adityakumarpuri/dead-frequency-a-ghost-in-the-machine-with-a-fuse-box-2pog</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of the strangest bugs in Dead Frequency was a ghost that had become too polite. The engine stores health from 0 to 1; the director API expects 0 to 100. At 75% health, the engine sent 0.75. The low-health guard compared that with 30, treated a healthy player as nearly dead, and downgraded every proposed hunt. Nothing crashed. The types were right, the units were wrong, and the tension disappeared at the boundary.&lt;/p&gt;

&lt;p&gt;Dead Frequency is a short survival-horror game about an abandoned facility, three dead relays, and an emergency operator that should not still be answering. You restore the relays, avoid the creature, and decide whether to release the voice or shut it down. It is Next.js, TypeScript, and Canvas 2D, not 3D.&lt;/p&gt;

&lt;p&gt;I wanted the AI to be a voice in the game, not the game itself. The shipped version makes no model calls. Every haunting event comes from a deterministic scripted fallback. The live Tinker path is tested against mocks, but it has never made a live request.&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%2Furbglzsusohyr70trk4t.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%2Furbglzsusohyr70trk4t.png" alt="The title screen" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;You start in the arrival bay of a facility whose emergency channel is still open. Three relays are dark, and the exit gate opens only when all three are restored. Between you and the relays is a creature that sees your flashlight from farther away and hears sprinting from farther than a walk.&lt;/p&gt;

&lt;p&gt;The loop is small. Restore three relays, avoid the creature, reach the console, then choose. The arrival bay is a sanctuary where the creature's senses are masked. The rest of the facility is not.&lt;/p&gt;

&lt;p&gt;The map is seeded, with five rooms, corridors, props, relays, and the console, all checked for solvability. The creature wakes after your second room or thirty seconds. It follows the walkable graph, so it can lose you but cannot walk through a wall or teleport.&lt;/p&gt;

&lt;p&gt;The HUD shows integrity, stamina, threat, relay pips, room, and clock. Pause, death, choice, and ending screens are included; touch devices get a stick and buttons.&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%2Fzh3iwh35wsitnylst7hn.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%2Fzh3iwh35wsitnylst7hn.png" alt="Gameplay" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scene is drawn and sounded in the browser: Canvas 2D passes for walls, floors, fixtures, relay glow, the creature, and grain; oscillators and filtered noise for audio. No remote art or sound files are downloaded. Audio starts after a click and can be muted.&lt;/p&gt;

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

&lt;p&gt;There is no hosted demo. I have not deployed this build, and a &lt;code&gt;render.yaml&lt;/code&gt; file is not a live URL. It runs locally with two commands, no API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ci
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:3000&lt;/code&gt;. Without Tinker configuration, the director badge reads &lt;code&gt;fallback&lt;/code&gt;, and the radio feed labels each scripted event as fallback.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/1241805818" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The 46.4-second clip shows the title, movement, flashlight, one real relay restoration, the labelled fallback radio feed, and pause. It is not a full playthrough: the other two relays, a creature encounter, death, and both endings are not shown. Those progression outcomes have simulation coverage, not video evidence.&lt;/p&gt;

&lt;p&gt;Every director request in this capture was intercepted with a scripted fallback response; no live model was called. The narration is an offline macOS &lt;code&gt;Samantha&lt;/code&gt; voice. The quiet background bed is locally synthesized, not captured game audio. &lt;a href="https://bubbles.adityalogs.xyz/o/dead-frequency-media/dead-frequency-demo.srt" rel="noopener noreferrer"&gt;English captions&lt;/a&gt;, the &lt;a href="https://bubbles.adityalogs.xyz/o/dead-frequency-media/narration.txt" rel="noopener noreferrer"&gt;transcript&lt;/a&gt;, and &lt;a href="https://github.com/puri-adityakumar/dead-frequency/blob/main/blogs/assets/media-notes.md" rel="noopener noreferrer"&gt;capture notes&lt;/a&gt; are included.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Paused&lt;/th&gt;
&lt;th&gt;Narrow screen&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr0tkji5ne7mx0a4tlly8.png" alt="Pause overlay" width="800" height="500"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsl05yh39lt8qak4hvx15.png" alt="Touch controls on a narrow screen" width="800" height="1731"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/puri-adityakumar/dead-frequency" rel="noopener noreferrer"&gt;puri-adityakumar/dead-frequency&lt;/a&gt;. It is currently private.&lt;/p&gt;

&lt;p&gt;Start here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src/shared/contracts.ts&lt;/code&gt;: the game/director boundary, including health units.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/game/sim.ts&lt;/code&gt; and &lt;code&gt;src/game/render.ts&lt;/code&gt;: simulation, creature, endings, and Canvas lighting.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/game/lighting.ts&lt;/code&gt;: the flashlight ray fan that stops at walls and sealed gates.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/lib/director/policy.ts&lt;/code&gt;: action whitelist, room/intensity ownership, low-health &lt;code&gt;hunt&lt;/code&gt; downgrade.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/lib/director/tinker.ts&lt;/code&gt; and &lt;code&gt;src/components/directorClient.ts&lt;/code&gt;: one-shot adapter, client timeout, labelled fallback.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scripts/smoke.mjs&lt;/code&gt; and &lt;code&gt;scripts/lighting-smoke.mjs&lt;/code&gt;: browser and lighting harnesses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code and this media package are in the private repository. I have not chosen a license or made a public release. There are no model weights or training code. The model's reply is parsed as JSON and mapped to an enum; it is data, not code.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  The simulation is boring on purpose
&lt;/h3&gt;

&lt;p&gt;The engine runs a fixed 1/60-second tick. The Canvas renderer reads the world without changing it, and the React HUD receives snapshots at most ten times a second. Movement and the creature use the same collision checks.&lt;/p&gt;

&lt;p&gt;The map is seeded and tested for solvability. Rooms sit on a coarse grid, corridors connect them, and the exit gate is a full ring whose only crossing is the exit approach, so sealing it cannot strand a relay. The lighting check renders fourteen deterministic scenes: five rooms off and on, three relay-restored states, and the exit gate open. A directional probe also checks that the beam brightens the space ahead more than the space behind.&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%2Fqfui6tzvsyy6a5mvci65.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%2Fqfui6tzvsyy6a5mvci65.png" alt="The flashlight beam" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The latest lighting pass moved average floor luminance in those raw captures from roughly 15-26 to roughly 52-73 on a 0-255 canvas scale. It improved palettes and fixtures, added a player rim highlight, clipped the flashlight beam, and reduced stacked vignettes. The check asserts a minimum brightness, shadow detail, and contrast. That is a smoke threshold, not a visual review.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://bubbles.adityalogs.xyz/o/dead-frequency-media/lighting-demo.mp4" rel="noopener noreferrer"&gt;12-second lighting comparison&lt;/a&gt; shows three labelled, isolated renderer fixtures before and after the pass. It is silent and is not gameplay footage.&lt;/p&gt;

&lt;h3&gt;
  
  
  The director only speaks at milestones
&lt;/h3&gt;

&lt;p&gt;The AI never runs in the frame loop. The engine emits five milestones: &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;room_enter&lt;/code&gt;, &lt;code&gt;relay_restored&lt;/code&gt;, &lt;code&gt;near_death&lt;/code&gt;, and &lt;code&gt;exit_ready&lt;/code&gt;. The browser sends a compact state with room, relays, health, flashlight, threat, milestone, and at most eight recent radio lines. There is no free-form prompt.&lt;/p&gt;

&lt;p&gt;The client allows one request in flight, keeps only the latest pending milestone, and aborts after nine seconds. On failure, timeout, or invalid output, it generates its own radio line and marks it &lt;code&gt;fallback&lt;/code&gt;. The game keeps playing.&lt;/p&gt;

&lt;p&gt;On the server, Zod checks the request and response. The upstream call uses one fixed HTTPS endpoint, one attempt, no redirects, a five-second body deadline, a 64 KiB cap, and 120 output tokens. When live, the model returns &lt;code&gt;{"action": "...", "message": "..."}&lt;/code&gt;. The action must be one of five: &lt;code&gt;whisper&lt;/code&gt;, &lt;code&gt;lights_flicker&lt;/code&gt;, &lt;code&gt;false_alarm&lt;/code&gt;, &lt;code&gt;hunt&lt;/code&gt;, &lt;code&gt;help&lt;/code&gt;. The server chooses the room and intensity.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hunt&lt;/code&gt; is downgraded to &lt;code&gt;whisper&lt;/code&gt; when health is at or below 30 or the milestone is &lt;code&gt;near_death&lt;/code&gt;. The server's scripted fallbacks never use &lt;code&gt;hunt&lt;/code&gt; and stay below 0.3 intensity. If the server itself is unreachable, the browser has a separate local whisper fallback. The model cannot directly change player health or position, teleport anyone, or permanently block progression. A hunt can temporarily sharpen the creature's senses and speed, so it still has gameplay consequences. The model cannot choose a URL or run generated code.&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%2Fetkm5ancdu1uyzq03z79.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%2Fetkm5ancdu1uyzq03z79.png" alt="The fallback label in the radio feed" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The boundary bug
&lt;/h3&gt;

&lt;p&gt;The health mismatch passed type checking because both values are numbers. Integration caught it when the server policy and engine snapshot disagreed about the scale. The fix was one conversion in &lt;code&gt;makeDirectorState&lt;/code&gt; and tests that assert the server receives 75, not 0.75, while the HUD stays fractional. This was an integration and mocked-policy bug, not a failure observed from a live model.&lt;/p&gt;

&lt;p&gt;A model boundary is not only a prompt. It is units, enums, ranges, timeouts, and a rule for when the answer never arrives. The most useful safety feature here is not a sentence in the system prompt. It is the finite action list and server-owned room and intensity.&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%2F9mxybkyqnr6ynst2o9wx.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%2F9mxybkyqnr6ynst2o9wx.png" alt="Illustrative architecture: local game, bounded director route, optional mocked Tinker path, and shipped scripted fallback. This is a source map, not a runtime trace." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel workstreams at max
&lt;/h3&gt;

&lt;p&gt;I ran the build as parallel workstreams: simulation and Canvas renderer; React shell, HUD, touch, and Web Audio; director API and hardening; and an independent browser QA harness. Each owned separate files; the shared contract kept them aligned. I integrated and fixed the seams.&lt;/p&gt;

&lt;p&gt;That split is why the health bug surfaced. The engine, UI, and server could each be correct alone and still disagree at the boundary. The QA workstream drove real Chrome with every director request intercepted, so no paid call could escape the browser.&lt;/p&gt;

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

&lt;p&gt;Once dependencies are installed and the local server is running, the scripted game needs no account, model key, or external service. Next.js, React, TypeScript, Node, and Zod are open components; Canvas 2D and Web Audio are browser standards. There are no remote art assets, so the look is editable source.&lt;/p&gt;

&lt;p&gt;OpenCode, an open-source coding-agent harness, was part of the actual build workflow: GLM workers built the renderer and UI, alongside Droid workers for the director and QA. That is a real use of an open agent harness, not evidence of live AI inside the game. The bounded runtime director is still an adapter tested with mocks.&lt;/p&gt;

&lt;p&gt;The model side needs a more careful sentence. Tinker is a hosted service. The repository contains an adapter and a strict contract, not open model weights. The account had zero checkpoints, and I did not train or fine-tune anything. I can show the source, fallback, validation, and tests; I cannot show a model result that does not exist.&lt;/p&gt;

&lt;p&gt;I have not chosen a license for the app yet. A public repository without a license is source-available, not open source. I will not call it open source until that file exists. The same care applies to the supplied Dark Sector reference: it informed the concept, but I did not copy its code because the archive has no explicit redistribution license. The reference is excluded from Git. Credit and permission are part of the work, not paperwork after it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ghost in the Machine
&lt;/h2&gt;

&lt;p&gt;The theme usually points at a machine that escapes its box. I wanted the opposite: a ghost that is present, responsive, and bounded.&lt;/p&gt;

&lt;p&gt;The director can whisper, flicker lights, send the creature toward a false alarm, sharpen a hunt, or help you catch your breath. It cannot rewrite the rules. The server owns room and intensity. The client owns the timeout. The engine owns damage and endings. The final choice belongs to the player.&lt;/p&gt;

&lt;p&gt;The machine is haunted, but it has a fuse box. The ghost can make the facility feel less predictable without becoming the author of the run. When no model is present, the fallback still speaks in the same world, with an honest &lt;code&gt;FALLBACK&lt;/code&gt; tag instead of a fake &lt;code&gt;LIVE&lt;/code&gt; badge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No live inference.&lt;/strong&gt; The Tinker account had zero checkpoints. No real model call, training, or paid inference happened; the shipped game uses fallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No fine-tune result.&lt;/strong&gt; No training, held-out evaluation, or baseline comparison. I am not claiming the Tinker fine-tuning category.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No hosted demo or deployment.&lt;/strong&gt; The app runs locally. &lt;code&gt;render.yaml&lt;/code&gt; is a blueprint, not a deployment. I am not claiming the Render category.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No public release or license yet.&lt;/strong&gt; The linked repository is private, and the app is not yet licensed open source. Repository packaging and documentation updates are dated November 1, 2026; the README records their scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests are split.&lt;/strong&gt; 56 unit tests, TypeScript, production build, fourteen deterministic lighting scenes, and 27 post-lighting Chrome smoke checks pass. The browser suite has no failures, warnings, skips, or blocked checks. Relay, death, and both endings have simulation coverage, but the short demo is not a full playthrough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope.&lt;/strong&gt; One creature, one facility, five rooms, no save system. The browser harness does not assert audio quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI is optional.&lt;/strong&gt; The live path is exercised with mocks, not a real checkpoint. Missing config, a busy gate, upstream error, or invalid output all produce the labelled fallback.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I am entering the Ghost in the Machine challenge. I am not entering the Tinker fine-tuning category, because I have no fine-tune or measured improvement. I am not entering the Render category, because I have not deployed. If those change before submission, I will update this section and demo rather than quietly widening a claim.&lt;/p&gt;



</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gamedev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Raincheck: the lazy, kind way to cancel a plan</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Sun, 25 Oct 2026 13:19:53 +0000</pubDate>
      <link>https://dev.to/adityakumarpuri/raincheck-the-lazy-kind-way-to-cancel-a-plan-3cg9</link>
      <guid>https://dev.to/adityakumarpuri/raincheck-the-lazy-kind-way-to-cancel-a-plan-3cg9</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cancelling a plan is a two-minute job that people stretch into an evening. You open the chat, type an apology, delete it, type a longer one, and then a second round of messages to agree on a new day. Most of us end up either over-explaining or going quiet. Raincheck is for the lazy middle path: say it once, say it kindly, offer one real next time, and let the other person answer with one click.&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%2Fup800c4j1n5gqkjv1aiw.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%2Fup800c4j1n5gqkjv1aiw.png" alt="Raincheck landing page with the fishing dock diorama" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Raincheck is a small writing desk for cancelling a plan without disappearing.&lt;/p&gt;

&lt;p&gt;You fill in four short sections. Who it's for and what the plan was. One replacement time, with an optional city. Your note. Then two checkboxes: "I've read this" and "I understand anyone with the link can read it". Only then does Raincheck make a ticket: one unguessable link that expires in 30 days. You copy it and send it wherever you already talk to that person. Raincheck sends nothing.&lt;/p&gt;

&lt;p&gt;The person on the other end opens the link and sees your note, the suggested time, and, if you picked a city, a real forecast for that day. They press &lt;strong&gt;Accept this suggestion&lt;/strong&gt;, and that's it. There's an optional &lt;code&gt;.ics&lt;/code&gt; file marked tentative, so the time can go into their calendar if they want it there. No account on either side. No invite. No calendar access.&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%2Fse4n3mul58d1ixaqrheh.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%2Fse4n3mul58d1ixaqrheh.png" alt="The writing desk, with the live ticket preview on the right" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who gets to be lazier.&lt;/strong&gt; The person cancelling skips the draft-delete-redraft loop and the "so when are you free?" thread that usually follows. The person receiving it skips the reply. A yes is one click. A no is just not clicking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kind, not sneaky.&lt;/strong&gt; The desk keeps saying the same things in small print: only say what's true, don't invent illness or emergencies, the date is a suggestion and nobody's availability has been checked. There's a &lt;strong&gt;Theatrical&lt;/strong&gt; tone for when you want to be silly, and it's labelled as a joke on both the draft and the ticket so nobody mistakes it for a real excuse.&lt;/p&gt;

&lt;p&gt;The landing page is a WebGL fishing dock I made earlier, ported to React with the shader, palette, fish and Bayer dithering kept as they were. Move the pointer and the fish follow. Click and the water ripples. It pauses when it scrolls offscreen, and it stands still if your system asks for reduced motion.&lt;/p&gt;

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

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/1239230557" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;A 55-second narrated walkthrough, recorded against a local build: write a note, pick Lisbon, see the real forecast, try AI drafting and get an honest "not configured", create the ticket, open it as the recipient, and accept.&lt;/p&gt;

&lt;p&gt;There's no hosted demo yet. The repo has a Render blueprint (&lt;code&gt;render.yaml&lt;/code&gt;, one Docker web service plus Postgres), but I haven't deployed it. It runs locally in two commands, shown below.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The recipient's view&lt;/th&gt;
&lt;th&gt;After one click&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy8yvmtziel0j63qgrd78.png" alt="Recipient ticket page" width="800" height="500"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc97tr33v0ov8pm8o6oq0.png" alt="Suggestion accepted" width="800" height="500"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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/puri-adityakumar" rel="noopener noreferrer"&gt;
        puri-adityakumar
      &lt;/a&gt; / &lt;a href="https://github.com/puri-adityakumar/raincheck" rel="noopener noreferrer"&gt;
        raincheck
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A small, open-source desk for cancelling plans kindly and offering one real next time. React + FastAPI, Open-Meteo forecasts, expiring share tickets, AI drafting off by default.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/puri-adityakumar/raincheck/blogs/assets/banner.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%2Fpuri-adityakumar%2Fraincheck%2FHEAD%2Fblogs%2Fassets%2Fbanner.png" alt="Raincheck: cancel kindly, suggest again" width="100%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;strong&gt;A small, open-source desk for calling off a plan honestly and offering one real next time.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/puri-adityakumar/raincheck/LICENSE" rel="noopener noreferrer"&gt;&lt;img alt="License: MIT" src="https://camo.githubusercontent.com/1addf2b3bfa1ab79f2467284161d87c8b79ceb65638e5d4c5931587d8628c601/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d326636663830"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/d59c92207d0d4f826a5d9ac49ee7cb459e8f987d9010f6ef8f791f955f2cb431/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f707974686f6e2d332e31322d326636663830"&gt;&lt;img alt="Python 3.12" src="https://camo.githubusercontent.com/d59c92207d0d4f826a5d9ac49ee7cb459e8f987d9010f6ef8f791f955f2cb431/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f707974686f6e2d332e31322d326636663830"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/477eea495f51fc14aa4ac9cfdcd21b84b3ec79bf6aa3c0961c2ce3597e38bf84/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e6f64652d32322532304c54532d326636663830"&gt;&lt;img alt="Node 22 LTS" src="https://camo.githubusercontent.com/477eea495f51fc14aa4ac9cfdcd21b84b3ec79bf6aa3c0961c2ce3597e38bf84/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e6f64652d32322532304c54532d326636663830"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/27ab6016f9deaccca574db1703afb8d27fedf66abf0e2cb5d972260c05e7dba9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d33333225323070617373696e672d336636623537"&gt;&lt;img alt="Tests: 332 passing" src="https://camo.githubusercontent.com/27ab6016f9deaccca574db1703afb8d27fedf66abf0e2cb5d972260c05e7dba9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d33333225323070617373696e672d336636623537"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/26afdbdc69c4c8c8bc678c9f6f00fad62977fd55cf7cfaf2e815164807d6caa3/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41492532306472616674696e672d6f6666253230627925323064656661756c742d623834613366"&gt;&lt;img alt="AI drafting: off by default" src="https://camo.githubusercontent.com/26afdbdc69c4c8c8bc678c9f6f00fad62977fd55cf7cfaf2e815164807d6caa3/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41492532306472616674696e672d6f6666253230627925323064656661756c742d623834613366"&gt;&lt;/a&gt;
&lt;/p&gt;




&lt;p&gt;Raincheck turns an awkward cancellation into one kind note and one concrete replacement time. You write and review the note, then create an unguessable, expiring link and share it yourself. The recipient opens it, sees the note and an optional real forecast, and accepts with one click. There are no accounts, no automatic messages, and no calendar writes.&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/puri-adityakumar/raincheck/blogs/assets/screenshots/04-preview.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%2Fpuri-adityakumar%2Fraincheck%2FHEAD%2Fblogs%2Fassets%2Fscreenshots%2F04-preview.png" alt="The writing desk with a live ticket preview" width="100%"&gt;&lt;/a&gt;
&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#features" rel="noopener noreferrer"&gt;Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#how-it-works" rel="noopener noreferrer"&gt;How it works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#quick-start" rel="noopener noreferrer"&gt;Quick start&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#configuration" rel="noopener noreferrer"&gt;Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#testing" rel="noopener noreferrer"&gt;Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#deployment" rel="noopener noreferrer"&gt;Deployment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#ai-drafting" rel="noopener noreferrer"&gt;AI drafting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#privacy-and-safety" rel="noopener noreferrer"&gt;Privacy and safety&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#project-structure" rel="noopener noreferrer"&gt;Project structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#project-status" rel="noopener noreferrer"&gt;Project status&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#contributing" rel="noopener noreferrer"&gt;Contributing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puri-adityakumar/raincheck#license-and-credits" rel="noopener noreferrer"&gt;License and credits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writing desk with live preview.&lt;/strong&gt; Recipient, plan, tone (Honest, Cozy, or a clearly labelled Theatrical joke), one replacement time, and your note. The ticket preview updates as you type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review before sharing.&lt;/strong&gt; Two explicit checks are required: you have read the current note, and you understand anyone with the link can read…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/puri-adityakumar/raincheck" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The parts worth opening first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;backend/app/services/tickets.py&lt;/code&gt;: token hashing, expiry, and the one-statement accept.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;backend/app/services/weather_tokens.py&lt;/code&gt;: the signed forecast proof.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;backend/app/services/generation/service.py&lt;/code&gt;: the open-weight model adapter, its prompt, and its output checks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;frontend/src/components/Composer.tsx&lt;/code&gt;: the writing desk and the review gate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;frontend/src/components/FishingDiorama.tsx&lt;/code&gt;: the dock.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Built It
&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%2Fg4z5ay8e6lskpj4urb5l.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%2Fg4z5ay8e6lskpj4urb5l.png" alt="How the pieces fit: React SPA, FastAPI, SQLite or Postgres, Open-Meteo, signed weather, and the optional open-weight AI adapter" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's one small service. A React 19 single-page app (Vite, TypeScript, Tailwind 4, Geist fonts bundled locally) talks to a FastAPI backend over &lt;code&gt;/api/*&lt;/code&gt;. In production the same container serves both, so there's no CORS to configure. Storage is SQLAlchemy over SQLite locally. The Render blueprint points it at Postgres, which I haven't run against a live server yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The open data: Open-Meteo.&lt;/strong&gt; City search and forecasts come from Open-Meteo's geocoding and forecast APIs. They're free and keyless, so there's no account and no key to leak. The server fetches the forecast for the chosen city and day, writes a plain summary like "Overcast, 17–25°C, 5% chance of rain", and signs it with HMAC-SHA256. The browser gets that signed proof and sends it back when it creates the ticket. The server checks the signature and the expiry before it puts any weather on the ticket. So a ticket can't show a forecast the server didn't fetch, and the attribution and fetch time are always printed next to it. If Open-Meteo is down, you get no weather, not invented weather.&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%2Frax6dgk4avebokkcuabl.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%2Frax6dgk4avebokkcuabl.png" alt="The forecast block with Open-Meteo attribution and fetch time" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The open-weight model: Qwen, through a Tinker adapter, switched off.&lt;/strong&gt; This is the part I want to be plain about. Raincheck has an AI drafting boundary, &lt;code&gt;POST /api/generate&lt;/code&gt;, built for small open-weight models. The adapter accepts only &lt;code&gt;Qwen/Qwen3-8B&lt;/code&gt;, &lt;code&gt;Qwen/Qwen3.5-4B&lt;/code&gt;, or &lt;code&gt;Qwen/Qwen3.5-9B&lt;/code&gt; through Tinker's sampling API, with an optional &lt;code&gt;tinker://&lt;/code&gt; fine-tuned adapter path. It caps replies at 256 tokens, runs at temperature 0.5, allows two requests at a time, and never retries a paid call on its own.&lt;/p&gt;

&lt;p&gt;The prompt treats every user field as data, not instructions. It forbids invented illness, emergencies, shared history, calendar access, or the other person's availability. It may mention weather only by quoting the signed summary word for word. Then the reply is checked anyway, after Unicode normalisation: no Markdown or HTML, no medical words, no "you're free then", no weather words outside the signed summary. A draft that fails comes back as a typed error, and the note box keeps whatever you'd already written. Even a draft that passes still needs the same two checkboxes before it can become a ticket.&lt;/p&gt;

&lt;p&gt;All of that is tested offline against an SDK double. None of it has called a real model. Live inference needs &lt;code&gt;AI_PROVIDER=tinker&lt;/code&gt;, a separate &lt;code&gt;TINKER_ALLOW_PAID_INFERENCE=true&lt;/code&gt;, credentials, and the pinned SDK, and every one of those is off. So with the default settings the endpoint returns &lt;code&gt;503 ai_unavailable&lt;/code&gt;, the health check reports &lt;code&gt;ai_generation: false&lt;/code&gt;, and the desk tells you so.&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%2Fo316dl7gu0ivlysq9nj5.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%2Fo316dl7gu0ivlysq9nj5.png" alt="AI drafting is not configured, and the desk says so" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd rather ship a desk that says "write your own" than one that shows a canned sample and calls it AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ticket.&lt;/strong&gt; A share token is 32 random bytes from &lt;code&gt;secrets.token_urlsafe&lt;/code&gt;. Only its SHA-256 hash is stored, so a copy of the database can't open anyone's ticket. Tickets expire after 30 days. Accepting is a single &lt;code&gt;UPDATE … WHERE accepted_at IS NULL&lt;/code&gt;, so two people tapping at once, or one person tapping twice, record one acceptance and keep the first time. The private "anything to help with drafting?" field never leaves the composer. The ticket request strips it before it's sent.&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%2Fc7fu4ovbj7osuq28am9o.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%2Fc7fu4ovbj7osuq28am9o.png" alt="The life of one ticket: write, review, create, share, accept" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.ics&lt;/code&gt; file is &lt;code&gt;STATUS:TENTATIVE&lt;/code&gt;, with no organiser, no attendees, and no &lt;code&gt;METHOD&lt;/code&gt;, so no calendar treats it as an invitation. Names and notes in any script, emoji included, are folded safely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tests.&lt;/strong&gt; &lt;code&gt;./checks.sh&lt;/code&gt; runs everything without network, installs, or AI calls: 234 backend tests, 32 adapter and tooling tests, and 66 frontend tests, plus Ruff, TypeScript, and a production build. Backend tests use throwaway SQLite databases and recorded Open-Meteo fixtures. The Docker image runs as a non-root user and refuses to start in production without a weather-signing secret.&lt;/p&gt;

&lt;p&gt;I built it with a coding agent doing the typing. Separate workers owned the frontend, backend, and AI adapter, and an independent reviewer went through the result. That review found six real issues, including text in unusual Unicode forms slipping past the output checks, and an older database schema that would have broken group tickets. All six were fixed before I published.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limits, said plainly.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI drafting is off. No live model has generated a note, and I'm not claiming any quality numbers.&lt;/li&gt;
&lt;li&gt;No fine-tune has run. The training script is a guarded dry run that refuses to spend money.&lt;/li&gt;
&lt;li&gt;Not deployed. The Render blueprint exists. Live Postgres, rate limits, and platform log redaction are still to do.&lt;/li&gt;
&lt;li&gt;One replacement time per ticket. No back-and-forth, no counter-proposal. That's on purpose, but it's still a limit.&lt;/li&gt;
&lt;li&gt;Anyone with the link can read the ticket. The desk says so before it makes one.&lt;/li&gt;
&lt;li&gt;Forecasts only reach about two weeks out. For a date further away, the ticket has no weather.&lt;/li&gt;
&lt;li&gt;Older databases from an earlier build are refused at startup, not migrated. That's safer than guessing, but it's manual.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Raincheck is built on things that are already open, so the expensive parts didn't need building.&lt;/p&gt;

&lt;p&gt;The forecast is the clearest case. Open-Meteo is free and needs no key, so a tiny side project can show real weather without an account, a bill, or a secret in the repo. Because the data is open, the server can fetch it, sign it, and stand behind it. The rest of the stack is open too: FastAPI, SQLAlchemy, React, Vite, Tailwind, and the Geist fonts under the OFL. And &lt;code&gt;.ics&lt;/code&gt; is an open standard, so "add it to your calendar" didn't need a Google or Microsoft integration.&lt;/p&gt;

&lt;p&gt;The model side is about what happens when I turn it on. A cancellation note is small, personal text: a name, a plan, sometimes a reason. With an open-weight model, I choose which model reads it and where it runs, and I can swap it. Qwen 3.5 4B is plenty for a 700-character note. I can also fine-tune it on a small, consented set of honest notes, so it learns "no fake excuses" from examples and not only from a prompt. A closed API gives me none of those choices: not the weights, not the version, and not a guarantee the model won't change underneath the safety checks.&lt;/p&gt;

&lt;p&gt;There's one more reason, and it's why I'm comfortable shipping with AI switched off. Because the model is just one swappable part behind a gate, the app doesn't depend on it. The desk works fully without a model. It doesn't fake one. And it's ready for an open model the day I decide to pay for inference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&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%2Fyp2ynoqko0lkihsleq73.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%2Fyp2ynoqko0lkihsleq73.png" alt="Things I didn't build, because open work already did them" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the person cancelling, the payoff is one message, not a thread. You write it once, with the new time already in it. For the person receiving it, a yes is one click and needs no reply.&lt;/p&gt;

&lt;p&gt;For me, the lazy payoff was everything I didn't build. No weather service, no calendar integrations, no messaging, no accounts, and no dependency on a closed model. Each of those is a whole project with its own bugs and secrets, and an open piece or an open standard covered it.&lt;/p&gt;

&lt;p&gt;I haven't timed anyone using it yet, so I won't put a number on it. The honest version: if you've ever spent twenty minutes on a two-line cancellation, this is for you.&lt;/p&gt;

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

&lt;p&gt;I'm not entering a partner category this week. Raincheck has a Render blueprint, but the app isn't deployed on Render. Its open-weight adapter targets Tinker, but the Tinker category asks for a measured fine-tune against a baseline, and I haven't run one. The narration in the demo is Microsoft Edge neural text-to-speech (the Andrew voice), not ElevenLabs. I'd rather say that than claim categories I haven't earned.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>Inkline — notes to diagrams, with Gemma, on your machine</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Sun, 18 Oct 2026 13:02:46 +0000</pubDate>
      <link>https://dev.to/adityakumarpuri/inkline-notes-to-diagrams-with-gemma-on-your-machine-3haa</link>
      <guid>https://dev.to/adityakumarpuri/inkline-notes-to-diagrams-with-gemma-on-your-machine-3haa</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A lecture, a system sketch, or a photo of a whiteboard should become a diagram without a closed API ever seeing the notes.&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%2Fsin01456uwq9qypqrbzx.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%2Fsin01456uwq9qypqrbzx.png" alt=" " width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Inkline is a desk that turns notes into a diagram.&lt;/p&gt;

&lt;p&gt;Paste lecture notes and you get a mind map. Paste “a client just asked for one more thing” and you get the decision as a flowchart. Describe a link shortener, an invoice, a launch week, or the service you are about to build, and you get an architecture diagram, a sequence, or a timeline. Drop a photo of a whiteboard or a page of handwritten notes and Gemma reads the picture. Under the drawing, “Change it…” takes a sentence like “add a Redis cache between API and DB” and updates the diagram in place, with undo, redo, and a short history. Simpler, Balanced, and Detailed change how much of the notes makes it onto the page. An open-weight model, Gemma 4 through Ollama, writes the Mermaid. The page draws it. You copy the source, copy a Markdown block for Notion, Obsidian, or GitHub, export SVG, PNG, or a vector PDF, or share a short link that opens the same drawing.&lt;/p&gt;

&lt;p&gt;A switch at the top chooses where that model runs. &lt;strong&gt;Private&lt;/strong&gt;, with a lock, means localhost Ollama: the notes stay on the machine. &lt;strong&gt;Cloud&lt;/strong&gt; means Ollama Cloud. The server picks the provider from the request, so the same desk can do both. If Private is selected and Ollama is not running, the page says to start &lt;code&gt;ollama serve&lt;/code&gt; and pull &lt;code&gt;gemma4&lt;/code&gt;, instead of failing in silence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who would pay.&lt;/strong&gt; The people already doing this by hand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Students who rewrite a chapter as boxes the night before an exam.&lt;/li&gt;
&lt;li&gt;Developers who have to explain a system and would rather not spend half an hour fighting Mermaid syntax.&lt;/li&gt;
&lt;li&gt;Freelancers and small teams who drop a diagram into a proposal, a README, or a change order.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Running it yourself is free. That is the point of an open model: the notes never have to leave the laptop, and there is no per-diagram bill. Themes, picture input, edits, history, and export are in that free desk. The thing I would charge for is the hosted desk, for someone who wants it in a browser and does not want to install Ollama. &lt;strong&gt;₹199 a month, or $5 a month&lt;/strong&gt;, for Cloud Gemma, higher limits, picture-to-diagram, and the history of a drawing. A team plan would be one shared Cloud budget for a lab or a small group. The pricing page in the app shows those three plans. It does not take a card. Pro and Team are not on sale.&lt;/p&gt;

&lt;p&gt;The tedious part is not the idea. It is getting a model to emit Mermaid that actually parses. Small open models are good at the shape of a system and bad at the punctuation. Inkline’s whole trick is a repair loop. If Mermaid rejects a draft, the parser error goes back to Gemma, and it tries again, up to three times. The drafts sit on the page, rejected ones included, so you can see what broke.&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%2F1asw1l5qnk6qh7v1oy3v.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%2F1asw1l5qnk6qh7v1oy3v.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/1236974437" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live desk: &lt;a href="https://inkline-lzyf.onrender.com" rel="noopener noreferrer"&gt;https://inkline-lzyf.onrender.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Six drawings Gemma made from real notes are in the gallery. Each link opens that drawing on the desk.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Repo: &lt;a href="https://cursor.com/codebase/puriadityakumar/Inkline/tree/main" rel="noopener noreferrer"&gt;InKline&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The repair loop lives in &lt;code&gt;src/lib/repair.ts&lt;/code&gt;, and the desk is one Next.js page.&lt;/p&gt;

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

&lt;p&gt;Gemma 4 is the only model. There is no closed-model fallback.&lt;/p&gt;

&lt;p&gt;Locally, the Next.js server talks to Ollama at &lt;code&gt;http://127.0.0.1:11434&lt;/code&gt;, model &lt;code&gt;gemma4&lt;/code&gt;. Cloud requests go to &lt;code&gt;https://ollama.com&lt;/code&gt; with &lt;code&gt;OLLAMA_API_KEY&lt;/code&gt;, usually &lt;code&gt;gemma4:31b&lt;/code&gt;. The desk sends &lt;code&gt;provider: "private"&lt;/code&gt; or &lt;code&gt;provider: "cloud"&lt;/code&gt; on each draw, and the server resolves that to a base URL. A private request never attaches the cloud key, and the key is not in the client bundle or the health check. The same client speaks Ollama’s OpenAI-compatible &lt;code&gt;/v1/chat/completions&lt;/code&gt; endpoint, and falls back to Ollama’s own &lt;code&gt;/api/chat&lt;/code&gt; if that path is missing. Pictures use the &lt;code&gt;images&lt;/code&gt; field on &lt;code&gt;/api/chat&lt;/code&gt;, because Gemma 4 is multimodal. If that call is rejected, Inkline asks Gemma to describe the picture in words and then runs the normal repair loop on the description, and the page says that is what happened.&lt;/p&gt;

&lt;p&gt;A picture is read once. Gemma lists the boxes and the arrows, and the desk shows that list as “Here's what I saw”: one row per arrow, with from, to, and a label. You can fix a row, add one, or remove one, and then press Draw. The drawing is made from those rows, not from a second look at the photo, so a wrong arrowhead is something you can see and correct before it becomes a diagram. The repair loop then rejects a draft whose arrows do not match the rows you confirmed. A chat edit sends the current Mermaid, the original notes, and the instruction through the same loop. Detail is a clause in the prompt: Simpler stays under about 8 nodes, Detailed keeps the secondary facts and stays under 24, and neither is allowed to invent facts that are not in the notes. The first draw from a picture stays Balanced, so the arrow check is not also fighting a detail edit. &lt;code&gt;OLLAMA_REASONING_EFFORT=none&lt;/code&gt; keeps a thinking model from spending the whole reply on a thought trace and returning no diagram.&lt;/p&gt;

&lt;p&gt;The prompt asks for one Mermaid fence and a specific diagram shape. The reply is rarely that clean, so extraction pulls the fence out of prose, generic code blocks, and Gemma’s thinking wrappers, and straightens curly quotes.&lt;/p&gt;

&lt;p&gt;Then Mermaid parses it on the server. &lt;code&gt;happy-dom&lt;/code&gt; is there because the parser expects a DOM. If parsing throws, that error string — the line, the token it wanted, the token it got — becomes the next user message, next to the broken source. The loop stops at three drafts, or sooner if one parses. The API streams each draft as it lands, so the page can show “Draft 1 was rejected” while Gemma is already on draft 2. If the browser’s Mermaid still disagrees with the server, that render error goes back too, using whatever attempts are left. A run that fails all three drafts leaves the previous drawing on the page. The rejected drafts stay in the rail so you can open them.&lt;/p&gt;

&lt;p&gt;The page is Next.js, TypeScript, and Tailwind. Diagram types are auto, mind map, flowchart, sequence, architecture, and timeline. Architecture is drawn as a flowchart with subgraphs, because that syntax survives a small model better than &lt;code&gt;architecture-beta&lt;/code&gt;. A share link is a compressed hash (&lt;code&gt;lz-string&lt;/code&gt;) of the type, the Mermaid, and the edit history, plus the notes when they still make the link shorter. PDF export traces the SVG with svg2pdf, so the file is vectors rather than a JPEG stuffed into a page. The Paper theme’s sample drawing is a baked SVG, so the first paint does not boot Mermaid.&lt;/p&gt;

&lt;p&gt;Tests mock the model. They check extraction, that a parser error is actually present in the follow-up prompt, that the loop stops at three, and that a dropped connection keeps the drafts already made. Playwright walks the desk twice, at a desktop width and at 375px, against that same mock: generate, edit and undo, keyboard use, export, the picture review, and the empty desk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limits, said plainly.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The public demo allows 10 draws a minute and 50 a day for an IP address, and 200 Cloud draws a day for the whole service (&lt;code&gt;DAILY_CLOUD_LIMIT&lt;/code&gt;). Private draws do not count against that Cloud cap. Without Upstash, those counters live in the server’s memory and reset when the process does.&lt;/li&gt;
&lt;li&gt;Notes cap at 20,000 characters. An edit is 500 characters. A picture is 4 MB, PNG, JPEG, WebP, or GIF. The model reply is capped around 2,500 tokens.&lt;/li&gt;
&lt;li&gt;A hidden company field and a same-origin check sit in front of the draw API. They are speed bumps, not a login.&lt;/li&gt;
&lt;li&gt;Detailed cannot add a service you did not mention. Simpler will drop a branch you might have wanted.&lt;/li&gt;
&lt;li&gt;The PDF is one page. A very wide map is scaled to fit, not split.&lt;/li&gt;
&lt;li&gt;Pro and Team are display only. Nothing on the pricing page charges a card.&lt;/li&gt;
&lt;li&gt;Gallery previews are drawn in the browser. If Mermaid fails there, the link to the desk still works.&lt;/li&gt;
&lt;li&gt;A picture is only as right as the arrow list you confirm. Draw uses those rows. It does not look at the photo again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Accuracy note.&lt;/strong&gt; The sample whiteboard was redrawn with gaps between the boxes and large arrowheads. The ink is Browser → CDN (HTTPS), CDN → API, API → Redis (cache), and API → Postgres (SQL). On 17 Oct 2026, three reads with &lt;code&gt;gemma4:31b&lt;/code&gt; each returned those four directions and those labels. That is 3 of 3. The check stays on the page anyway: Draw uses the rows you confirm, and a wrong row is what gets drawn.&lt;/p&gt;

&lt;p&gt;Render hosts the hosted desk. &lt;code&gt;render.yaml&lt;/code&gt; points the service at Ollama Cloud, because Render has no local GPU. &lt;code&gt;GET /api/health&lt;/code&gt; is the health check. Per-IP and daily rate limits keep a public URL from burning through the Ollama key, and the key never leaves the server.&lt;/p&gt;

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

&lt;p&gt;A diagram tool that sends every note to a closed API is a bad deal for the people this is for.&lt;/p&gt;

&lt;p&gt;A student mapping a chapter does not want that chapter stored on someone else’s account. A freelancer sketching a client’s system is holding someone else’s architecture. Private is the default, with a lock on the switch: localhost Ollama, and the prompt stays on the machine. Cloud is a deliberate second position for someone who has not installed Ollama, using the same open model. A whiteboard photo and a handwritten page take the same path. The picture is another kind of note, not a reason to call a closed vision API.&lt;/p&gt;

&lt;p&gt;Cost is the other half. The hosted price only works if inference is close to free. Gemma is open weight, so a month of diagrams does not depend on a closed model’s token price going up. I can swap &lt;code&gt;gemma4:e2b&lt;/code&gt; on a laptop for &lt;code&gt;gemma4:31b&lt;/code&gt; in the cloud, or a later fine-tune aimed at Mermaid, by changing &lt;code&gt;OLLAMA_MODEL&lt;/code&gt;. The repair loop is the same either way. A closed API would charge for the failed drafts too, and I would not be allowed to tune the model when it keeps making the same bracket mistake.&lt;/p&gt;

&lt;p&gt;Open is what makes the side hustle possible: private by default, cheap enough to run, and not stuck on one vendor’s model.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma.&lt;/strong&gt; Gemma 4, through Ollama, writes every diagram: mind maps, flowcharts, sequences, timelines, and architecture, including the picture path. Local tags (&lt;code&gt;gemma4&lt;/code&gt;, &lt;code&gt;gemma4:e2b&lt;/code&gt;, &lt;code&gt;gemma4:e4b&lt;/code&gt;) and Ollama Cloud (&lt;code&gt;gemma4:31b&lt;/code&gt;) are the same client. No closed model is called.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render.&lt;/strong&gt; &lt;code&gt;render.yaml&lt;/code&gt; deploys the Next.js desk. The hosted service uses Ollama Cloud because Render has no local GPU. &lt;code&gt;GET /api/health&lt;/code&gt; is the health check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of a .tech Domain.&lt;/strong&gt; The live desk runs at [ADD .TECH LINK], pointed at the Render service. &amp;lt;!-- delete this bullet if no .tech domain --&amp;gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>While your build runs, walk</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Sun, 11 Oct 2026 16:13:53 +0000</pubDate>
      <link>https://dev.to/adityakumarpuri/while-your-build-runs-walk-44cl</link>
      <guid>https://dev.to/adityakumarpuri/while-your-build-runs-walk-44cl</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;A GitHub Actions run already knows how long it is. You start it, and then you sit there, watching a bar that has already done the counting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://build-break.onrender.com/" rel="noopener noreferrer"&gt;Build Break&lt;/a&gt; takes that length and puts it on the phone. You paste a public repository, press start, and the screen becomes a clock that stays awake. You leave. If the street is worth keeping, you take one photo. When the run finishes, or when you decide you are done, a short journal entry is waiting.&lt;/p&gt;

&lt;p&gt;The first screen says the whole thing in one breath. While your build runs, walk. Snap what you see. An open model writes the entry when you get back.&lt;/p&gt;

&lt;p&gt;The screen is a glance. The walk is the rest of it.&lt;/p&gt;

&lt;p&gt;Paste &lt;code&gt;owner/repo&lt;/code&gt;, or a github.com URL, and the walk follows the newest run that is still going. The browser calls the public GitHub API from the phone, with no token. The countdown is the median of the last ten successful runs, minus the time already spent, and it never goes below zero. A repository with no successful history gets a fifteen-minute guess, and the page says so. "No recent successful runs, so this timer is a fifteen minute guess. The walk still ends when the run finishes." It checks that run every thirty seconds.&lt;/p&gt;

&lt;p&gt;If the guess runs out and the build is still going, the headline changes. "Go walk," becomes "Still out." Under it: the build has not finished.&lt;/p&gt;

&lt;p&gt;If nothing is in progress, the walk becomes a demo and one line says why. "No run in progress on that repo. This is a demo walk." A rate limit says "GitHub rate limit. This is a demo walk." A malformed URL stays on the start screen: "That does not look like a public GitHub repo." A repository GitHub cannot find stays an error too: "That repository is not public, or it does not exist." I would rather be told than be sent outside on a timer that was never real.&lt;/p&gt;

&lt;p&gt;Leave the field blank and it is a demo from the first step, anywhere from ten to sixty minutes. That is the walk in the pictures below. The end is the start plus those minutes, so a reload keeps counting.&lt;/p&gt;

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

&lt;p&gt;It is live at &lt;a href="https://build-break.onrender.com/" rel="noopener noreferrer"&gt;build-break.onrender.com&lt;/a&gt;. Open it on a phone. On a desktop the same page sits in a narrow column, because this is a thing you carry.&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%2Fu406jhnjyx4c5zfqeiss.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%2Fu406jhnjyx4c5zfqeiss.png" alt="Build Break on a phone. The line under the title says: while your build runs, walk." width="780" height="1688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press start with the field empty. The phone says go, and then a number. This one is 32:59, marked demo, with the screen kept awake.&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%2Fq5f5ez5nyua9m3finou7.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%2Fq5f5ez5nyua9m3finou7.png" alt="The walk screen. Go walk, back in 32:59, demo. Capture, or end the walk." width="780" height="1688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Capture is there if you want a picture. End walk is there if you come back early. In this demo there is no model key, so the note under the photo is written on the machine, one sentence from a small local set:&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%2Fv8s8zsqf9ydt02lnofxn.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%2Fv8s8zsqf9ydt02lnofxn.png" alt="A stand of trees over pavement, and the note: the way ahead is quiet." width="780" height="1688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The way ahead is quiet: pavement, trunks, and a pale sky.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sentence is a stand-in. It lets you see the shape of a note before Gemma is pointed at a real street. It is not a record of a walk I took.&lt;/p&gt;

&lt;p&gt;When the demo clock hits zero, the journal writes the entry in the same voice. You can also end the walk yourself and get the entry then. This one is stamped 11 Oct 2026, 12:45 PM IST, one minute, demo.&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%2Fmpwzqk71x7afreaclktw.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%2Fmpwzqk71x7afreaclktw.png" alt="The journal. This week: 1 walk, 1 min outside while CI ran." width="780" height="1688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I went out Sunday afternoon, 12:45 PM, and stayed for a minute. The way ahead is quiet: pavement, trunks, and a pale sky. It was a short loop, and a good one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The line I keep looking at is the one above the entry. This week: one walk, one minute outside while CI ran.&lt;/p&gt;

&lt;p&gt;Point the app at Gemma and those stand-in sentences are replaced by whatever was actually in the photo, the weather if you allowed it, and how the run ended. On the laptop that model is Gemma 3 4B, because it can see. On Ollama Cloud it is Gemma 4 31B. The prompts stay the same.&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%2F0q9qp3xk9auuuq3mo0o6.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%2F0q9qp3xk9auuuq3mo0o6.png" alt="On a wide screen, Build Break stays a phone-width column in the middle of the black." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The license is MIT. The app is Next.js and TypeScript. Walks, notes, and the journal live in one SQLite file. Photos sit on disk next to it, under &lt;code&gt;DATA_DIR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;GitHub is called from the browser. The model is one chat-completions client. The three screens are a start page, a walk, and a journal.&lt;/p&gt;

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

&lt;p&gt;The open piece is the model, and the model is one HTTP call.&lt;/p&gt;

&lt;p&gt;Build Break speaks the OpenAI chat completions API. Three environment variables aim it, and nothing else is hardcoded: &lt;code&gt;LLM_BASE_URL&lt;/code&gt;, &lt;code&gt;LLM_MODEL&lt;/code&gt;, and &lt;code&gt;LLM_API_KEY&lt;/code&gt;. With no key, you get the local stand-in in the screenshots above. &lt;code&gt;LLM_PROVIDER=mock&lt;/code&gt; keeps that path even when a key is sitting in the environment.&lt;/p&gt;

&lt;p&gt;For a real note I point it at Ollama on the laptop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LLM_PROVIDER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ollama
&lt;span class="nv"&gt;LLM_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:11434/v1
&lt;span class="nv"&gt;LLM_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gemma3:4b
&lt;span class="nv"&gt;LLM_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ollama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key has to be non-empty, or the app assumes you have not chosen a model. Ollama ignores the value.&lt;/p&gt;

&lt;p&gt;The photo needs a model that can see. Gemma 3 at 270M and at 1B is text only, so those builds cannot read a picture. Gemma 3 4B can. It is asked for one short, warm sentence about what someone on a walk would notice: plants, animals, sky, the life of the street. No guessing past what is in the frame. The image is sent before the text, which is the order Gemma prefers.&lt;/p&gt;

&lt;p&gt;The same model writes the journal. Three to five sentences, first person, drawn from the time of day, the minutes outside, the notes, and the last few entries. A fact it was not given is left out. Weather appears only if you allowed location. A pattern gets a sentence only when a word actually shows up again. Plain, warm, no hashtags.&lt;/p&gt;

&lt;p&gt;Gemma 3, including the 4B, is released by Google under the &lt;a href="https://ai.google.dev/gemma/terms" rel="noopener noreferrer"&gt;Gemma Terms of Use&lt;/a&gt; (last modified 1 April 2026). The appendix on that page lists Gemma 3. It is Google's own license, with a prohibited-use policy. It is not the Apache License, and it is not an OSI open-source license. I use it because it can see a photo on a machine I already have, and that limit belongs in the post.&lt;/p&gt;

&lt;p&gt;When the laptop cannot hold the model, the same client talks to Ollama Cloud at &lt;code&gt;https://ollama.com/v1&lt;/code&gt;. The model id is &lt;code&gt;gemma4:31b&lt;/code&gt;, taken from Ollama's own list. The &lt;code&gt;:cloud&lt;/code&gt; alias is for a signed-in local Ollama, and it is the wrong string to send to the cloud API. Gemma 4 31B takes text and images and returns text, so one model still does both jobs. Gemma 4 is under the &lt;a href="https://ai.google.dev/gemma/apache_2" rel="noopener noreferrer"&gt;Apache License 2.0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Changing the model is one line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LLM_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gemma3:12b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point &lt;code&gt;LLM_BASE_URL&lt;/code&gt; at another host and the prompts stay put.&lt;/p&gt;

&lt;p&gt;GitHub is called from the browser, so the request uses the phone's address. Unauthenticated calls are about sixty an hour. That is why a 403 becomes a demo walk with a sentence attached. Photos are resized in the browser to about 1024 pixels on the long edge, saved as JPEG, and only then uploaded to our own API. They are not sent to a model until an endpoint is configured. On localhost, that endpoint is Ollama on the same machine.&lt;/p&gt;

&lt;p&gt;The journal clock is the browser's time zone. That is why the demo stamp reads IST. The page asks for location once: add the weather from where you are. A no is remembered. Coordinates are not stored.&lt;/p&gt;

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

&lt;p&gt;The picture is of the street you are standing on. I wanted it read on the machine, with no account and no fee for each photo.&lt;/p&gt;

&lt;p&gt;Gemma 3 4B through local Ollama does that. If the sentence is too thin, I change &lt;code&gt;LLM_MODEL&lt;/code&gt;. The prompts stay. A closed vision API would have been a shorter afternoon, and then every walk would have been a metered call, with the photo leaving the phone for someone else's account. A walk every time a build starts is a bad match for a meter.&lt;/p&gt;

&lt;p&gt;Location is a choice for the same reason. Allow it, and the server asks &lt;a href="https://open-meteo.com/" rel="noopener noreferrer"&gt;Open-Meteo&lt;/a&gt; for the current conditions. There is no weather key. The sentence comes back in a fixed shape, like "28°C and clear." The latitude and longitude are not written into the journal. Refuse the prompt, and the entry never brings the weather up. Open-Meteo does see the coordinates when you allow them. A model provider does not receive the GPS. The database does not keep a record of where you were.&lt;/p&gt;

&lt;p&gt;Gemma 4 is there for the day the laptop is not enough. Because it is Apache 2.0, I can host it, or let Ollama Cloud host it, on the same three variables. The Render blueprint points the host at &lt;code&gt;gemma4:31b&lt;/code&gt; on Ollama Cloud, so the phone in your pocket does not have to be the computer that runs the weights.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Best Use of Gemma.&lt;/strong&gt; The one-line note and the journal entry are the model's work. On the laptop that is Gemma 3 4B, under the Gemma Terms of Use, because it can see. On Ollama Cloud it is Gemma 4 31B, under Apache 2.0. Either way the app is a thin timer around an open-weight model, and the model is one line to change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Render.&lt;/strong&gt; Build Break is a Node server with a SQLite file and a folder of photos. That wants a disk that survives a restart. &lt;code&gt;render.yaml&lt;/code&gt; is the blueprint: the web service on the starter plan, &lt;code&gt;DATA_DIR&lt;/code&gt; at &lt;code&gt;/var/data&lt;/code&gt;, a 1 GB disk, and the model variables in the host environment. A persistent disk needs a paid instance. A free instance forgets the journal on restart. The app is live at &lt;a href="https://build-break.onrender.com/" rel="noopener noreferrer"&gt;build-break.onrender.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>A code edit can pass the request and still drop something the prompt never said</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Sun, 11 Oct 2026 12:23:15 +0000</pubDate>
      <link>https://dev.to/adityakumarpuri/a-code-edit-can-pass-the-request-and-still-drop-something-the-prompt-never-said-2a</link>
      <guid>https://dev.to/adityakumarpuri/a-code-edit-can-pass-the-request-and-still-drop-something-the-prompt-never-said-2a</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/kaggle-2026-09-23"&gt;Kaggle Benchmarking Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.kaggle.com/benchmarks/puriadityakumar/resolve" rel="noopener noreferrer"&gt;Resolve&lt;/a&gt; measures a small code change. I wanted a score for the failure I keep seeing: the edit does the thing that was asked, and drops a behavior the prompt never said again.&lt;/p&gt;

&lt;p&gt;Each item is one Python function and one requested edit. The model returns only the new function. The score is how many of 16 items are fully resolved, as a float from 0 to 1. A half-fix scores zero on that item.&lt;/p&gt;

&lt;p&gt;There are two tasks. &lt;strong&gt;resolve&lt;/strong&gt; checks the new behavior and the old behavior the prompt never restated. &lt;strong&gt;resolve-plus&lt;/strong&gt; adds an edge suite the prompt never showed. The two tasks are separate generations, so the second score can land above the first.&lt;/p&gt;

&lt;p&gt;The result this post is about: GPT-6.1 Sol and GPT-5.6 Luna are the only models at 0.9375 on both tasks. The task page rounds that to 0.94. Several other models match 0.9375 on resolve and then score lower on resolve-plus.&lt;/p&gt;

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

&lt;p&gt;The prompt shows the function, the one change, and two sample inputs. Anything the model writes besides the new function is thrown away. Hidden tests then run in two lists.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fail-to-pass&lt;/strong&gt; is the new behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pass-to-pass&lt;/strong&gt; is behavior the prompt never repeated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An item counts only when every test in both lists passes. A timeout, a crash, a missing function, or an empty suite is a failure. One failed test fails the item.&lt;/p&gt;

&lt;p&gt;resolve-plus uses the same rule and adds the edge suite. It is a new sample of the 16 items, not a second grade of the first sample.&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%2Fdkgfln5gustipbq155e6.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%2Fdkgfln5gustipbq155e6.png" alt="Resolved means every hidden test passes. The prompt, the model, the hidden tests, and the score run in order. resolve checks both lists. resolve-plus adds an edge suite." width="800" height="1040"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Inspiration
&lt;/h3&gt;

&lt;p&gt;This is the rule &lt;a href="https://www.swebench.com/" rel="noopener noreferrer"&gt;SWE-bench&lt;/a&gt; uses for a resolved issue, scaled down to functions I wrote. The paper is Jimenez et al., &lt;a href="https://arxiv.org/abs/2310.06770" rel="noopener noreferrer"&gt;SWE-bench: Can Language Models Resolve Real-World GitHub Issues?&lt;/a&gt; (ICLR 2024).&lt;/p&gt;

&lt;p&gt;SWE-bench does not give credit because a patch compiles or because the new test passes. The issue is resolved only when the fail-to-pass tests and the pass-to-pass tests all pass. Resolve keeps that bar and drops the rest of the scaffolding: no scraped repository, no gold patch in the prompt, no tests written by the model. The failure worth reading is an edit that does the asked change and drops an invariant the prompt did not say again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Models Tested
&lt;/h2&gt;

&lt;p&gt;I started with one model from each of four families, then ran the Claude, GPT, and Gemini lines the proxy would serve, plus the open models listed below. Frontier and open models are charted separately so a single average cannot hide which group is carrying the score.&lt;/p&gt;

&lt;p&gt;Twenty-three models have both scores. A model is on a chart only when both have finished. Blue is resolve. Orange is resolve-plus. Charts are sorted by resolve-plus.&lt;/p&gt;

&lt;p&gt;Frontier: Claude Haiku 4.5, Haiku 5.5, Sonnet 5, Sonnet 5.5, Opus 5.5, GPT-5.4 mini, GPT-5.6 Luna, GPT-5.6 Sol, GPT-6.1 Sol, Gemini 3.5 Flash, Gemini 3.5 Flash-Lite, Gemini 3.6 Flash, Gemini 3.7 Flash, and Gemini 3.8 Flash.&lt;/p&gt;

&lt;p&gt;Open: Gemma 4 31B, Gemma 4 26B, gpt-oss 20B, gpt-oss 120B, DeepSeek-R1, GLM-5, Qwen 3 235B, Qwen 3 Coder 480B, and Qwen 3 Next 80B Instruct.&lt;/p&gt;

&lt;p&gt;Grok 4.5 and Grok 4.6 are in the Kaggle model list. The proxy returned model not found for both, so they are not on the charts. Qwen 3 Next 80B Thinking timed out three times on that same proxy, the last time after 3 of 16 items, so it is off the charts too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Findings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  All 23 models
&lt;/h3&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%2Fgcs8cu7fm3286pns22dd.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%2Fgcs8cu7fm3286pns22dd.png" alt="All models. Blue is resolve, orange is resolve-plus. Sorted by resolve-plus." width="800" height="804"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GPT-6.1 Sol and GPT-5.6 Luna are tied at the top, 0.9375 on both tasks. That is 15 of 16 items, twice.&lt;/p&gt;

&lt;p&gt;The next group matches the 0.9375 resolve score and lands at 0.875 on resolve-plus: DeepSeek-R1, Claude Sonnet 5.5, and Claude Opus 5.5. Each of them resolved 15 items on the first task and 14 on the second.&lt;/p&gt;

&lt;p&gt;gpt-oss 20B is the bar that runs the other way, 0.75 then 0.875. The second task is a new sample, so a higher resolve-plus score is a different generation, not the edge suite helping. The floor of the combined chart is Qwen 3 235B and Gemma 4 26B, both at 0.6875 on resolve-plus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontier models
&lt;/h3&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%2Fwn0il3iktkni9bs5f61z.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%2Fwn0il3iktkni9bs5f61z.png" alt="Frontier models. Blue is resolve, orange is resolve-plus." width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The frontier chart is the same tie at the top, then a clean step down.&lt;/p&gt;

&lt;p&gt;Opus 5.5 and Sonnet 5.5 both open at 0.9375 and close at 0.875. GPT-5.6 Sol opens at the same 0.9375 and closes at 0.8125, a two-item drop. Sonnet 5 holds 0.875 on resolve and falls to 0.75 on resolve-plus.&lt;/p&gt;

&lt;p&gt;Haiku 4.5 and Haiku 5.5 sit on the same pair, 0.875 and 0.875. On these 16 functions the newer small model did not move the score. Gemini 3.5 Flash and Gemini 3.6 Flash sit with them. Gemini 3.5 Flash-Lite is the frontier floor, 0.75 on both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open models
&lt;/h3&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%2F2490wfv9ek8a2pkek8i5.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%2F2490wfv9ek8a2pkek8i5.png" alt="Open models. Blue is resolve, orange is resolve-plus." width="799" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DeepSeek-R1 leads the open chart at 0.9375 and 0.875, level with Opus 5.5 and Sonnet 5.5 on the frontier chart. Gemma 4 31B is the only open model that holds 0.875 on both tasks. Gemma 4 26B is last, 0.75 then 0.6875.&lt;/p&gt;

&lt;p&gt;Qwen 3 Coder 480B, at 0.875 and 0.8125, beats the larger general Qwen 3 235B, at 0.8125 and 0.6875. gpt-oss 120B and Qwen 3 Next 80B Instruct are flat at 0.8125. gpt-oss 20B is the open-chart exception: its second sample scores 0.875 after a 0.75 first sample.&lt;/p&gt;

&lt;h3&gt;
  
  
  Score against list price
&lt;/h3&gt;

&lt;p&gt;The runs did not store tokens or a dollar bill, so this is not measured cost per item. The horizontal axis is the published output price per million tokens, taken from a &lt;a href="https://www.developersdigest.tech/blog/frontier-model-api-pricing-june-2026" rel="noopener noreferrer"&gt;rate card last updated 8 October 2026&lt;/a&gt;. That card reports the rates against the &lt;a href="https://www.anthropic.com/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;, &lt;a href="https://openai.com/api/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;, and &lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt; pricing pages. Expensive is on the left. Cheap is on the right. A line connects models from the same provider. Open models are off this chart, and so are Gemini 3.6 Flash and Gemini 3.7 Flash, because that rate card does not list them. Haiku 5.5 is the under-100k rate. These prompts are one short function.&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%2Fzdrnehyqk4orhqhcua4k.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%2Fzdrnehyqk4orhqhcua4k.png" alt="resolve-plus against published output price. Expensive models are on the left, cheaper models on the right. GPT-5.6 Luna and GPT-6.1 Sol are tied at 0.9375, with Luna much cheaper." width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GPT-5.6 Luna is the efficient point: 0.9375 at $1.20 per million output tokens. GPT-6.1 Sol matches that score at $10. Claude Haiku 5.5 holds 0.875 at $0.50, the same score as Haiku 4.5 at $5 and Opus 5.5 at $20. Sonnet 5 and Sonnet 5.5 share the $10 output price. Sonnet 5.5 is the one at 0.875.&lt;/p&gt;

&lt;h3&gt;
  
  
  What a flat leaderboard hides
&lt;/h3&gt;

&lt;p&gt;The first pass was one model from each of four families: Claude Haiku 4.5, Gemma 4 31B, GPT-5.4 mini, and Gemini 3.7 Flash. Each resolved 14 of 16 items. The leaderboard looks flat until the second row. Haiku and Gemma stay at 0.875 on resolve-plus. GPT-5.4 mini and Gemini 3.7 Flash fall to 0.8125.&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%2F6adqf0lu95zl5ddf4lq7.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%2F6adqf0lu95zl5ddf4lq7.png" alt="resolve is 0.875 for every model. resolve-plus stays 0.875 for Claude Haiku 4.5 and Gemma 4 31B, and falls to 0.8125 for GPT-5.4 mini and Gemini 3.7 Flash." width="799" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those two drops are different events.&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;ring_push&lt;/code&gt;, each of those four models, on both tasks, made the requested change and failed a behavior the prompt never restated. Fail-to-pass passed. Pass-to-pass did not. A looser score would have counted those edits. This one does not. That miss was stable across those eight runs.&lt;/p&gt;

&lt;p&gt;GPT-5.4 mini’s lower resolve-plus score is the extra suite. On &lt;code&gt;truncate&lt;/code&gt;, the requested change and the old behavior both passed, and the edge suite failed. Haiku and Gemma did not lose an item that way.&lt;/p&gt;

&lt;p&gt;Gemini 3.7 Flash’s lower resolve-plus score is a different generation of the same items. &lt;code&gt;collapse_name&lt;/code&gt; passed the first time and failed the second. The extra suite was not what moved it.&lt;/p&gt;

&lt;p&gt;One number is hiding three things: an edit that breaks an unstated invariant, an edit that misses an edge the prompt never showed, and a second sample of the same item that comes out differently.&lt;/p&gt;

&lt;p&gt;One generation was enough to move &lt;code&gt;collapse_name&lt;/code&gt; from a pass to a fail. A one-shot score is a noisy picture of a model that is close to the line. The next run I would make is more than one sample per item.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Benchmark
&lt;/h2&gt;

&lt;p&gt;The public benchmark is &lt;a href="https://www.kaggle.com/benchmarks/puriadityakumar/resolve" rel="noopener noreferrer"&gt;Resolve on Kaggle&lt;/a&gt;. It is version 1, Apache 2.0, and marked public. The leaderboard on that page shows the first four models. The task pages list every run, including ones that did not finish. A model is in the charts here only when both scores finished.&lt;/p&gt;

&lt;p&gt;The two tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.kaggle.com/benchmarks/tasks/puriadityakumar/resolve/1" rel="noopener noreferrer"&gt;resolve&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.kaggle.com/benchmarks/tasks/puriadityakumar/resolve-plus/1" rel="noopener noreferrer"&gt;resolve-plus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suggested citation: Aditya Kumar Puri. Resolve. &lt;a href="https://www.kaggle.com/benchmarks/puriadityakumar/resolve/versions/1" rel="noopener noreferrer"&gt;https://www.kaggle.com/benchmarks/puriadityakumar/resolve/versions/1&lt;/a&gt;, 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Jimenez, Carlos E., John Yang, Alexander Wettig, Shunyu Yao, Kexin Pei, Ofir Press, and Karthik Narasimhan. “SWE-bench: Can Language Models Resolve Real-World GitHub Issues?” ICLR 2024. &lt;a href="https://arxiv.org/abs/2310.06770" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2310.06770&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SWE-bench. &lt;a href="https://www.swebench.com/" rel="noopener noreferrer"&gt;https://www.swebench.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Kaggle Community Benchmarks. &lt;a href="https://www.kaggle.com/benchmarks" rel="noopener noreferrer"&gt;https://www.kaggle.com/benchmarks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;kaggle-benchmarks Python library. &lt;a href="https://github.com/Kaggle/kaggle-benchmarks" rel="noopener noreferrer"&gt;https://github.com/Kaggle/kaggle-benchmarks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google AI. “Introducing Community Benchmarks on Kaggle.” &lt;a href="https://dev.to/googleai/introducing-community-benchmarks-on-kaggle-35nc"&gt;https://dev.to/googleai/introducing-community-benchmarks-on-kaggle-35nc&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Frontier model API pricing, last updated 8 October 2026. &lt;a href="https://www.developersdigest.tech/blog/frontier-model-api-pricing-june-2026" rel="noopener noreferrer"&gt;https://www.developersdigest.tech/blog/frontier-model-api-pricing-june-2026&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Anthropic pricing. &lt;a href="https://www.anthropic.com/pricing" rel="noopener noreferrer"&gt;https://www.anthropic.com/pricing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenAI API pricing. &lt;a href="https://openai.com/api/pricing" rel="noopener noreferrer"&gt;https://openai.com/api/pricing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gemini API pricing. &lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;https://ai.google.dev/gemini-api/docs/pricing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>kagglechallenge</category>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>I built an agent that asks permission before it touches anything</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Sun, 30 Aug 2026 18:25:43 +0000</pubDate>
      <link>https://dev.to/adityakumarpuri/i-built-an-agent-that-asks-permission-before-it-touches-anything-299c</link>
      <guid>https://dev.to/adityakumarpuri/i-built-an-agent-that-asks-permission-before-it-touches-anything-299c</guid>
      <description>&lt;p&gt;Every agent demo I watched this month had the same ending: the agent clicked "confirm" on itself. Nobody in the video batted an eye. The narrator called it "fully autonomous" and moved on.&lt;/p&gt;

&lt;p&gt;That's the part I can't get past. An agent that never stops isn't autonomous, it's just unattended.&lt;/p&gt;

&lt;p&gt;So for the WeMakeDevs × TrueFoundry Agent Harness Hackathon, I built Recap: a research-paper autopsy you can drive. You hand it a paper, it dissects it, and every time it wants to do something irreversible, it stops and waits for you. The agent supervises papers. You supervise the agent.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/puri-adityakumar/openwrite" rel="noopener noreferrer"&gt;https://github.com/puri-adityakumar/openwrite&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The job I gave it
&lt;/h2&gt;

&lt;p&gt;Research papers are a triage problem. You don't read one paper, you triage forty, and most of them don't deserve the forty minutes. The job I gave the agent: take an arXiv URL or a PDF, and come back with the parts a human actually needs. Not a summary. A workup.&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%2Fp4mnvcrtec4nzkbrpl3a.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%2Fp4mnvcrtec4nzkbrpl3a.png" alt=" " width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One click from landing to cockpit. The demo credentials sit right there because a tired judge is a real user segment.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What comes back is five surfaces: a live Trail of everything the agent did, a Coverage grid, a Claims↔Evidence table, an Authors tab, and a downloadable Audit. The Claims↔Evidence table is the one I'd want as a reviewer. Every claim in the paper, next to the evidence the agent found for it, and the gaps in between.&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%2F3fy3f9o7x4saxnox9s6u.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%2F3fy3f9o7x4saxnox9s6u.png" alt=" " width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The dashboard. First paint shows a populated run, not an empty state. Empty states are where demos go to die.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The pause is the product
&lt;/h2&gt;

&lt;p&gt;Here's the feature I actually care about: three approval gates. Verify, Publish, Save. Before the agent does anything irreversible, it pauses and asks. Not a toast you can ignore. A full stop.&lt;/p&gt;

&lt;p&gt;The Verify gate is deliberately obnoxious. Before the paper's untrusted code runs in a sandbox, you have to type the repo owner's name AND hold a button for three full seconds. That's not friction by accident. That's friction on purpose. Papers are not trusted code, and "run the repo this paper links to" deserves a speed bump.&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%2F0w0y5vz8bxxrm30wf8wt.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%2F0w0y5vz8bxxrm30wf8wt.png" alt=" " width="750" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Verify gate. Type the owner, hold for three seconds. If it feels excessive, try imagining what the code does.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Annoying answer: most agent safety demos are theater. A checkbox nobody reads is consent, not control. A pause you have to hold open is control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring it up
&lt;/h2&gt;

&lt;p&gt;The stack is boring on purpose: Next.js, Postgres, Redis, one &lt;code&gt;docker compose up&lt;/code&gt;, everything local. The interesting wiring is the agent harness.&lt;/p&gt;

&lt;p&gt;One command, no clone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @truefoundry/trueforge@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives me a TrueForge server on localhost:8790. Then I registered GMI Cloud as the model provider, pointed at their Anthropic-compatible endpoint with a custom base_url, running MiniMax-M3. No adapter code. A curl with a manifest, done.&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%2Fjm7p2neqjke781l5id1z.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%2Fjm7p2neqjke781l5id1z.png" alt=" " width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The cockpit mid-run. Real SSE events streaming in: turn created, deltas, tool calls. The Trail on the left is the agent's actual receipt.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My app talks to TrueForge through its REST + SSE API. &lt;code&gt;lib/trueforge.ts&lt;/code&gt; ships an HttpTrueForgeClient, and the events come over the wire for real: &lt;code&gt;turn.created&lt;/code&gt;, &lt;code&gt;model.message.delta&lt;/code&gt;, &lt;code&gt;tool.approval_required&lt;/code&gt;, &lt;code&gt;turn.done&lt;/code&gt;. When a gate fires, my UI surfaces the pause, and your allow/deny decision routes back to TrueForge through its resume contract. Nothing in the hot path is mocked. There's a smoke script that proves the "pong" in the transcript came from the actual model.&lt;/p&gt;

&lt;h2&gt;
  
  
  What TrueForge took off my plate
&lt;/h2&gt;

&lt;p&gt;This is the list I'd have underestimated a week ago:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The agent runtime itself: turn lifecycle, retries, cancellation. I would have hand-rolled a state machine and shipped at least two race conditions.&lt;/li&gt;
&lt;li&gt;Event streaming. SSE with backpressure and reconnection is a project, not a file.&lt;/li&gt;
&lt;li&gt;Model-provider plumbing. Swapping GMI in without writing an adapter was the single best "oh, that's it?" moment of the hackathon.&lt;/li&gt;
&lt;li&gt;The approval-pause/resume contract. The hard part of human-in-the-loop isn't the UI, it's freezing a running turn and resuming it correctly after a human decides. That's a solved problem here, and I just used it.&lt;/li&gt;
&lt;li&gt;Sandbox handling, with a local fallback when no cloud sandbox is configured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wrote the surfaces: gates, trail, audit, the cockpit UI. TrueForge ran the agent underneath them. That division of labor is the whole pitch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it broke
&lt;/h2&gt;

&lt;p&gt;The low point: the demo ran fine until tool execution. Daytona, the sandbox provider, kept refusing the write that publishes a sandbox snapshot. The docs made it sound like &lt;code&gt;write:sandboxes&lt;/code&gt; was enough. It isn't. You also need &lt;code&gt;write:snapshots&lt;/code&gt;, and nothing tells you that until the API returns a permission error mid-demo.&lt;/p&gt;

&lt;p&gt;The fix I reached for was TrueForge's local sandbox fallback, which auto-engages when no cloud sandbox provider is configured. The demo lived. And to be clear about the trade: tool execution behind the gate is skipped right now, and the harness says so honestly in the audit as &lt;code&gt;sandbox.disabled&lt;/code&gt;. It doesn't pretend. The audit shows the gap, which is exactly the behavior I built the whole product around.&lt;/p&gt;

&lt;p&gt;Honestly, that's the part I'm proudest of: the failure mode is visible, not hidden.&lt;/p&gt;

&lt;h2&gt;
  
  
  The review loop nobody asked about
&lt;/h2&gt;

&lt;p&gt;Quick one, because it shaped everything: every substantive change landed through a Qodo-reviewed pull request. Thirteen PRs, all reviewed. The approval-gates PR came back with 17 findings across two review passes, including a race condition in my event reducer and a missing TTL guard that could have double-approved a gate. Every finding triaged before merge. The loop became the norm: PR, review, fix, merge. 237 unit tests and 54 E2E tests green at the end of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole run
&lt;/h2&gt;

&lt;p&gt;If you'd rather watch than read:&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;180 seconds, the whole run, no cuts.&lt;/em&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%2Fydj8dul1nf2z9r8m0tqj.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%2Fydj8dul1nf2z9r8m0tqj.png" alt=" " width="800" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The audit at the end. Every action logged, every decision attributed. If the agent did it, the audit can prove it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The agents are coming either way. The interesting question isn't whether they can do the work. It's whether you can see them doing it.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/puri-adityakumar/openwrite" rel="noopener noreferrer"&gt;https://github.com/puri-adityakumar/openwrite&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Biryani, With Love: A Deccan-Inspired Interactive Atlas</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Sun, 02 Aug 2026 11:31:41 +0000</pubDate>
      <link>https://dev.to/adityakumarpuri/biryani-with-love-a-deccan-inspired-interactive-atlas-4amb</link>
      <guid>https://dev.to/adityakumarpuri/biryani-with-love-a-deccan-inspired-interactive-atlas-4amb</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, Perfect Landing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Biryani, With Love&lt;/strong&gt; is an interactive editorial atlas about the many regional traditions that share the name biryani.&lt;/p&gt;

&lt;p&gt;I chose biryani because comfort food is rarely only about flavour. It can hold a city, a family ritual, a migration story, a celebration, and a memory of home in the same pot. The landing page follows that idea from Hyderabad through India and across the Indian Ocean.&lt;/p&gt;

&lt;p&gt;The experience includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A research-led map of eight Indian biryani traditions&lt;/li&gt;
&lt;li&gt;A method lab comparing kachchi, pakki, and one-pot cooking sequences&lt;/li&gt;
&lt;li&gt;A guided loop through fourteen traditions across nine countries&lt;/li&gt;
&lt;li&gt;A comparison tool for grain, method, flavour, heat, perfume, tang, and richness&lt;/li&gt;
&lt;li&gt;A historical route connecting language, court kitchens, regional grains, and Indian Ocean migration&lt;/li&gt;
&lt;li&gt;A research shelf that makes the supporting sources visible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The visual system is inspired by a contemporary Deccan pavilion. Saffron, peacock green, pomegranate red, plum, brass, and ivory create the palette. The hero uses CSS-built arches, minarets, jali panels, steam, rice grains, garnish, and an open copper handi.&lt;/p&gt;

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

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/puri-adityakumar/embed/MYJZObG?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/puri-adityakumar/pen/MYJZObG" rel="noopener noreferrer"&gt;Open the full CodePen&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Finding the right metaphor
&lt;/h3&gt;

&lt;p&gt;My first version treated the page like a clean editorial index. It was readable, but it did not carry enough of the warmth, ceremony, and visual richness of the subject.&lt;/p&gt;

&lt;p&gt;The breakthrough was to stop treating biryani as a list of recipes and instead design an edible atlas. Each section became a different way to read the same dish: geography, cooking sequence, migration, flavour, and memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Researching the architecture
&lt;/h3&gt;

&lt;p&gt;The heritage direction was grounded in specific references rather than a generic ornamental style.&lt;/p&gt;

&lt;p&gt;I studied the &lt;a href="https://hyderabad.telangana.gov.in/tourist-place/charminar/" rel="noopener noreferrer"&gt;Charminar architecture described by Hyderabad District&lt;/a&gt;, particularly its four-sided arches, minarets, carved rings, and Qutb Shahi lotus details. I also looked at a &lt;a href="https://www.metmuseum.org/art/collection/search/453343" rel="noopener noreferrer"&gt;sixteenth-century Indian jali in The Metropolitan Museum of Art&lt;/a&gt;, where stars, hexagons, and interlocking geometry create a play of light and pattern.&lt;/p&gt;

&lt;p&gt;Those ideas became solid-color architectural frames, small lattice details, geometric rules, and the central handi illustration. I kept the interface modern by using strong editorial typography, square cards, clear hierarchy, and restrained motion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Correcting the map
&lt;/h3&gt;

&lt;p&gt;The first map used a simplified world dataset and looked noticeably wrong. I replaced it with the official &lt;a href="https://surveyofindia.gov.in/pages/outline-maps-of-india" rel="noopener noreferrer"&gt;Survey of India 1:16m outline vector&lt;/a&gt;, including the island territories.&lt;/p&gt;

&lt;p&gt;The eight city points are projected from geographic coordinates instead of being positioned by eye. The map remains a cultural navigation device, but its outline and locations now have a defensible source.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the interactions
&lt;/h3&gt;

&lt;p&gt;The project is a single responsive HTML document with inline CSS and vanilla JavaScript. CodePen separates those same three layers into its HTML, CSS, and JavaScript panels.&lt;/p&gt;

&lt;p&gt;JavaScript is used purposefully for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switching regional map stories&lt;/li&gt;
&lt;li&gt;Updating cooking method diagrams&lt;/li&gt;
&lt;li&gt;Running and pausing the global biryani loop&lt;/li&gt;
&lt;li&gt;Filtering traditions by geography&lt;/li&gt;
&lt;li&gt;Comparing two traditions&lt;/li&gt;
&lt;li&gt;Updating the historical route&lt;/li&gt;
&lt;li&gt;Respecting reduced-motion preferences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The page contains no raster food photography. The hero, diagrams, decorative architecture, and interface details are created with HTML, CSS, and inline vector geometry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility and usability
&lt;/h3&gt;

&lt;p&gt;I wanted the atlas to remain useful beyond its visual impact. It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Semantic sections and heading structure&lt;/li&gt;
&lt;li&gt;Keyboard-operable SVG map points&lt;/li&gt;
&lt;li&gt;Visible focus states&lt;/li&gt;
&lt;li&gt;A skip link&lt;/li&gt;
&lt;li&gt;Live regions for changing content&lt;/li&gt;
&lt;li&gt;Reduced-motion support&lt;/li&gt;
&lt;li&gt;Responsive layouts for narrow screens&lt;/li&gt;
&lt;li&gt;Source links for the research claims&lt;/li&gt;
&lt;li&gt;Clear notes where flavour scores are interpretive rather than scientific measurements&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Am Proud Of
&lt;/h2&gt;

&lt;p&gt;I am most proud that the page does not rank biryanis or declare one authentic winner. It gives each tradition enough context to feel distinct while showing the shared ritual underneath: rice, patience, adaptation, and memory.&lt;/p&gt;

&lt;p&gt;The final result feels like a love letter, a research object, and a modern landing page at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Collaboration
&lt;/h2&gt;

&lt;p&gt;I used OpenAI Codex as a collaborative coding, research, and iteration tool. I directed the concept, selected the biryani theme, reviewed each visual pass, identified inaccuracies, and made the final design decisions. The implementation and research sources were checked and refined through that collaboration.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>DUM: Breaking the Seal on Hyderabadi Biryani with Pure CSS</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Sun, 02 Aug 2026 06:19:47 +0000</pubDate>
      <link>https://dev.to/adityakumarpuri/dum-breaking-the-seal-on-hyderabadi-biryani-with-pure-css-28d0</link>
      <guid>https://dev.to/adityakumarpuri/dum-breaking-the-seal-on-hyderabadi-biryani-with-pure-css-28d0</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, CSS Art&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspiration
&lt;/h2&gt;

&lt;p&gt;Hyderabadi dum biryani is more than a dish to me—it is a ritual. The sealed handi, the slow charcoal heat, the suspense before the atta crust is broken, and the first rush of saffron, mint, birista, and spice all feel inseparable from the experience.&lt;/p&gt;

&lt;p&gt;I wanted to turn that moment into an interactive midnight poster: Hyderabad’s skyline behind a copper handi, with the food hidden until the viewer breaks the seal.&lt;/p&gt;

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

&lt;p&gt;Click &lt;strong&gt;BREAK THE SEAL&lt;/strong&gt; to lift the lid and reveal the four biryani layers.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/editor/puri-adityakumar/embed/019fc0e2-b269-7487-be30-6e380fcb9616?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

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

&lt;p&gt;The artwork is built with HTML and CSS only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A native &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; control stores the open and closed states.&lt;/li&gt;
&lt;li&gt;CSS &lt;code&gt;:has()&lt;/code&gt; coordinates the seal crack, lid lift, layer reveal, steam, labels, embers, and state-aware copy.&lt;/li&gt;
&lt;li&gt;Rice grains, mint leaves, birista, spices, meat, copper patina, flour dust, the skyline, and the moon are all CSS shapes.&lt;/li&gt;
&lt;li&gt;There are no images, SVGs, canvas, JavaScript, gradients, or frameworks.&lt;/li&gt;
&lt;li&gt;A mobile composition and &lt;code&gt;prefers-reduced-motion&lt;/code&gt; keep the piece responsive and accessible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reveal is deliberately choreographed:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;seal cracks → lid lifts → layers separate → labels arrive → steam settles&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;The hardest part was keeping the illustration detailed without losing the strong poster silhouette. I iterated on three areas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Material:&lt;/strong&gt; hammered copper marks, soot, flour residue, dough cracks, and print texture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Depth:&lt;/strong&gt; curved food layers, overlapping grains, steam arches, and ingredient silhouettes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Motion:&lt;/strong&gt; a staged opening sequence rather than making every element animate at once.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most satisfying decision was using a semantic HTML control for the interaction. The artwork still works with a keyboard, and disabling motion does not hide the final state.&lt;/p&gt;

&lt;p&gt;I used Codex as an iterative coding and visual-critique partner. I directed the concept, cultural references, composition, and final decisions, while the agent helped implement and test the CSS system.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’m proud of
&lt;/h2&gt;

&lt;p&gt;The finished piece stays intentionally constrained: one HTML file, one CSS file, a small palette, and no image assets. The interaction adds drama, but CSS remains the star of the submission.&lt;/p&gt;

&lt;p&gt;Thanks for taking a look—and remember: patience is the final ingredient.&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>css</category>
    </item>
    <item>
      <title>AI Storyline Generator</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Sun, 14 Sep 2025 21:49:45 +0000</pubDate>
      <link>https://dev.to/adityakumarpuri/ai-storyline-generator-3flp</link>
      <guid>https://dev.to/adityakumarpuri/ai-storyline-generator-3flp</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-ai-studio-2025-09-03"&gt;Google AI Studio Multimodal Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I created AI Storyline Generator, a web app that turns a set of images and a short text prompt into a cohesive narrative. It helps writers and artists quickly build stories or prototypes by offering three modes: Surprise Me (short story with optional cover art), Light Novel (full chapter with illustrations), and Manga (panel art).&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Demo Video&lt;/strong&gt;&lt;br&gt;
  &lt;iframe src="https://www.youtube.com/embed/d9XCAK-FJoM" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;br&gt;
&lt;strong&gt;Screenshots&lt;/strong&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fzp0hr8a28v8of4af7app.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.amazonaws.com%2Fuploads%2Farticles%2Fzp0hr8a28v8of4af7app.png" alt="Screenshot-1" width="800" height="674"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F6z5bkwgq0dpvv3u8t7pk.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.amazonaws.com%2Fuploads%2Farticles%2F6z5bkwgq0dpvv3u8t7pk.png" alt="Screenshot-2" width="799" height="677"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Ftjjwiu2uqx951s8arkrr.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.amazonaws.com%2Fuploads%2Farticles%2Ftjjwiu2uqx951s8arkrr.png" alt="Screenshot-3" width="800" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  LINK
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://ai.studio/apps/drive/1LfupIv14tZuSoTqDvHD6yHBoHzgc0bgG" rel="noopener noreferrer"&gt;Applet Link 🌱&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Note: I was not able to publish the app due to billing issues. Please accept the published app link. Thankyou.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Google AI Studio
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Used gemini-2.5-flash-image-preview for vision–language tasks like story generation and all image outputs.&lt;/li&gt;
&lt;li&gt;Used gemini-2.5-flash for Manga mode to reliably create structured JSON scripts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Multimodal Features
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Image-to-Story Generation: Combines multiple images and text into a connected plot.&lt;/li&gt;
&lt;li&gt;Narrative-Aware Illustration: Generates key-scene illustrations directly from the AI-written story.&lt;/li&gt;
&lt;li&gt;Automated Manga Pipeline: Creates a full manga page by chaining image analysis, JSON scripting, and panel art generation&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>ai</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Efficient Architecture 101: Is Your Upload Strategy Actually Efficient?</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Tue, 26 Aug 2025 17:15:06 +0000</pubDate>
      <link>https://dev.to/adityakumarpuri/efficient-architecture-101-is-your-upload-strategy-actually-efficient-j17</link>
      <guid>https://dev.to/adityakumarpuri/efficient-architecture-101-is-your-upload-strategy-actually-efficient-j17</guid>
      <description>&lt;h2&gt;
  
  
  Before we start...
&lt;/h2&gt;

&lt;p&gt;So, why this series? I've recently jumped straight into the complexities of system design, and I think the best approach to learning is by writing blogs and sharing what I have learned. My goal is to create simple blogs with humor and a lot of diagrams. Please note, I'm still learning, so if you're experienced and find something that can be improved or is incorrect, please share your thoughts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: A Real-World Bottleneck
&lt;/h2&gt;

&lt;p&gt;Not long ago, I was digging through a codebase and found a classic file upload setup. The user would select a file in their browser, the browser would send it to our backend server, and &lt;em&gt;then&lt;/em&gt; the server would upload it to a cloud storage bucket like AWS S3.&lt;/p&gt;

&lt;p&gt;It worked, but it was far from efficient. The architecture looked something like this:&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.amazonaws.com%2Fuploads%2Farticles%2F0a5qvwoztmbdjrss4ddu.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.amazonaws.com%2Fuploads%2Farticles%2F0a5qvwoztmbdjrss4ddu.png" alt=" " width="381" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  So, approach has some serious drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's Slow for the User:&lt;/strong&gt; The file has to make two trips over the internet. The user is stuck waiting for both to complete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It Hogs Server Resources:&lt;/strong&gt; Imagine 100 users uploading large videos at once. Your server is suddenly trying to process gigabytes of data, choking its memory, CPU, and bandwidth. This is a classic scalability nightmare.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It Can Be More Expensive:&lt;/strong&gt; You're paying for the bandwidth twice – once for the data coming into your server, and again for it going out to the cloud storage. Ouch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Slightly Better Approach (That's Actually Worse)
&lt;/h2&gt;

&lt;p&gt;So, to fix the speed issue, why not just upload directly from the browser? Modern tools like the AWS SDK for JavaScript let you do just that. The flow seems simpler and faster.&lt;/p&gt;

&lt;p&gt;But this comes with a giant, flashing, red security warning. To allow the browser to upload, you have to embed your secret access keys directly in the frontend code. This is like leaving the master key to your entire house under the doormat.&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.amazonaws.com%2Fuploads%2Farticles%2Fft9src89bcir9prpplfh.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.amazonaws.com%2Fuploads%2Farticles%2Fft9src89bcir9prpplfh.png" alt=" " width="322" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyone with a little know-how can open their browser's developer tools, find your keys, and gain full, unrestricted access to your storage bucket. It's a disaster waiting to happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Solution: Pre-Signed URLs
&lt;/h2&gt;

&lt;p&gt;This is where we get the best of both worlds: the speed of a direct upload and the security of a server-managed process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;pre-signed URL&lt;/strong&gt; is a special, temporary, and secure link that your server generates. It gives a user permission to perform a &lt;em&gt;specific action&lt;/em&gt; (like uploading a file with a specific name) directly to your cloud storage, but only for a limited time.&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.amazonaws.com%2Fuploads%2Farticles%2Frufi0ch96t3i72jbne5p.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.amazonaws.com%2Fuploads%2Farticles%2Frufi0ch96t3i72jbne5p.png" alt=" " width="719" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the browser first asks the server for permission. The server then generates a temporary, pre-signed URL and sends it back. The browser uses this special URL to upload the file directly to cloud storage. The server's only job is to authorize the request, not to handle the heavy file transfer itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, Why prefer pre-singed url for uploads?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Security:&lt;/strong&gt; Your cloud storage credentials are never exposed on the client-side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Performance:&lt;/strong&gt; Files are uploaded directly to cloud storage, reducing latency for the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increased Scalability:&lt;/strong&gt; Offloads the bandwidth and processing load from your server, allowing it to handle more application requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Optimization:&lt;/strong&gt; Reduces egress bandwidth costs from your server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use Short-Lived URLs:&lt;/strong&gt; Always configure pre-signed URLs with the shortest possible expiration time (e.g., 5-15 minutes) to minimize the risk of misuse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Utilize Multipart Uploads:&lt;/strong&gt; For large files, leverage multipart uploads. This involves generating multiple pre-signed URLs to upload the file in chunks, which improves reliability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement Resumable Uploads:&lt;/strong&gt; In case of network interruptions, a robust implementation should allow the upload to resume from the last successfully uploaded part, rather than restarting the entire process.&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
