<?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: Andre Gomes</title>
    <description>The latest articles on DEV Community by Andre Gomes (@derivativador).</description>
    <link>https://dev.to/derivativador</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%2F2814255%2Fc5e2998c-fc49-456a-9467-2c6bcea1991f.jpg</url>
      <title>DEV Community: Andre Gomes</title>
      <link>https://dev.to/derivativador</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/derivativador"/>
    <language>en</language>
    <item>
      <title>Ripple Relay — Give what you actually have</title>
      <dc:creator>Andre Gomes</dc:creator>
      <pubDate>Sun, 06 Sep 2026 21:37:22 +0000</pubDate>
      <link>https://dev.to/derivativador/ripple-relay-give-what-you-actually-have-1n</link>
      <guid>https://dev.to/derivativador/ripple-relay-give-what-you-actually-have-1n</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-09-03"&gt;Weekend Challenge: Generosity Edition&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Generosity can feel like an all-or-nothing decision: donate more, volunteer an entire afternoon, or wait until life is less busy. I wanted a smaller starting point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ripple Relay turns the capacity you actually have into a plan you can follow through on.&lt;/strong&gt; Set your available time, money and energy, choose the causes you care about, and get up to three small actions. They can involve care, skills, attention, useful goods, time or money.&lt;/p&gt;

&lt;p&gt;The constraint that matters is the combined budget. Three individually affordable suggestions are still a bad plan if they add up to more time or money than you offered. Ripple Relay keeps the whole plan within those limits.&lt;/p&gt;

&lt;p&gt;There is no guilt score or leaderboard. Zero minutes is a valid answer: the app gives you a quiet empty state instead of inventing capacity.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://acg0606.github.io/ripple-relay/" rel="noopener noreferrer"&gt;Try Ripple Relay in your browser&lt;/a&gt;&lt;/strong&gt; — no account or installation needed.&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%2Fh2vv6yhs0v8y1a0xgbpf.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%2Fh2vv6yhs0v8y1a0xgbpf.png" alt="Ripple Relay: capacity controls alongside a three-step generosity plan" width="800" height="773"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try this 30-second walkthrough:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep the defaults: &lt;strong&gt;45 minutes, $0, medium energy, Community and Education&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Build my ripple&lt;/strong&gt;. The plan suggests captioning a community video, checking in with someone isolated, and amplifying a verified request: &lt;strong&gt;40 minutes total, $0&lt;/strong&gt;, across three ways of helping.&lt;/li&gt;
&lt;li&gt;Change energy to &lt;strong&gt;Low&lt;/strong&gt; and rebuild. Demanding tasks leave the plan.&lt;/li&gt;
&lt;li&gt;Set time to &lt;strong&gt;0&lt;/strong&gt;. You get no actions and no pressure.&lt;/li&gt;
&lt;li&gt;Restore some time and use &lt;strong&gt;Copy private plan&lt;/strong&gt;. The text explicitly says it is a plan, not completed impact.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The catalog contains illustrative action ideas and estimates, not live volunteer openings or verified requests. Before acting, confirm the current need, consent, organization and local costs.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/acg0606/ripple-relay" rel="noopener noreferrer"&gt;Source code and reproducible checks on GitHub&lt;/a&gt;&lt;/strong&gt; — MIT licensed, with no runtime dependencies.&lt;/p&gt;

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

&lt;p&gt;The app uses semantic HTML, CSS and JavaScript modules. I kept the planning engine separate from the interface so its decisions could be tested without a browser.&lt;/p&gt;

&lt;p&gt;The algorithm first excludes actions that exceed the available time, money or energy level. It ranks the remaining actions using cause alignment, preferred mode and hand-authored planning weights. A greedy selection then favors different ways of helping while tracking the remaining time and money. Energy is a per-action limit, not a cumulative budget.&lt;/p&gt;

&lt;p&gt;Those weights are deliberately modest in meaning: &lt;strong&gt;fit is a planning heuristic, not measured impact&lt;/strong&gt;. The app does not claim it has discovered the optimal way to help, verified a nonprofit or caused a donation.&lt;/p&gt;

&lt;p&gt;Privacy shaped the architecture. The profile stays in browser local storage; the app has no analytics, remote API, payment flow or contact feature. Copying happens only on a click, and a selectable text fallback handles browsers that deny clipboard access. The static host may keep its ordinary request logs.&lt;/p&gt;

&lt;p&gt;Verification caught practical edge cases: a malformed saved profile could break startup, zero time originally became an artificial minimum, and denied clipboard access needed a useful fallback. Those are fixed. &lt;strong&gt;All 16 automated tests pass&lt;/strong&gt;, including budget checks across &lt;strong&gt;2,331 combinations&lt;/strong&gt; of the visible controls. A separate real Chrome pass checks persistence, zero capacity, a 375px mobile layout, clipboard behavior and loading under a project subpath.&lt;/p&gt;

&lt;p&gt;Development began during this challenge's entry window. &lt;strong&gt;OpenAI Codex assisted with implementation, editing and testing.&lt;/strong&gt; The running app does not call an AI model.&lt;/p&gt;

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

&lt;p&gt;I am entering the &lt;strong&gt;overall challenge&lt;/strong&gt;. This version does not use Snowflake, Solana, ElevenLabs or Google AI.&lt;/p&gt;

&lt;p&gt;My goal is simple: make the next act of generosity small enough to do, while being honest about what software can and cannot prove.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
    <item>
      <title>The Compatibility Cliff That Made HACKOPS Lose Its Execution Workspace</title>
      <dc:creator>Andre Gomes</dc:creator>
      <pubDate>Mon, 17 Aug 2026 20:00:23 +0000</pubDate>
      <link>https://dev.to/derivativador/the-compatibility-cliff-that-made-hackops-lose-its-execution-workspace-58h2</link>
      <guid>https://dev.to/derivativador/the-compatibility-cliff-that-made-hackops-lose-its-execution-workspace-58h2</guid>
      <description>&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%2F3cjeeo8cigtjvmmcd8ny.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%2F3cjeeo8cigtjvmmcd8ny.png" alt="HACKOPS compatibility fix — DEV Summer Bug Smash" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Clear the Lineup&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;HACKOPS is a local opportunity-production workspace. It keeps research, human approvals, tasks, generated artifacts, validation, and submission readiness together so a user can follow an opportunity from “GO” to a real delivery.&lt;/p&gt;

&lt;p&gt;The portfolio view worked, but the execution workspace could disappear completely on a browser that supported the application's architecture while lacking two newer convenience APIs. A user could choose a project and arrive at an empty area with no stage, tasks, evidence, artifacts, or recovery controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug Fix or Performance Improvement
&lt;/h2&gt;

&lt;p&gt;Two compatibility assumptions stopped the render pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the shared HTML-escaping helper called &lt;code&gt;String.prototype.replaceAll&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the structured evidence renderer called &lt;code&gt;Object.hasOwn&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I reproduced the issue with an automated Chrome test that removes both APIs before any application JavaScript runs and then opens the real route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/#execution/dev-summer-bug-smash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before the fix, the test observed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Workspace rendered&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stage badges&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workspace characters&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser error&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String(...).replaceAll is not a function&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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%2Fpqmmdma4c8dic2cy7zr2.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%2Fpqmmdma4c8dic2cy7zr2.png" alt="Before: the execution workspace fails to render when replaceAll and Object.hasOwn are unavailable" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem was not a missing feature. It was a broken navigation outcome in an existing codebase: after selecting a project, the user lost the whole execution context.&lt;/p&gt;

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

&lt;p&gt;The escaping helper now uses global regular expressions. They preserve the same ordered replacements without requiring &lt;code&gt;replaceAll&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- .replaceAll('&amp;amp;', '&amp;amp;amp;')
- .replaceAll('&amp;lt;', '&amp;amp;lt;')
- .replaceAll('&amp;gt;', '&amp;amp;gt;')
- .replaceAll('"', '&amp;amp;quot;')
- .replaceAll("'", '&amp;amp;#039;');
&lt;/span&gt;&lt;span class="gi"&gt;+ .replace(/&amp;amp;/g, '&amp;amp;amp;')
+ .replace(/&amp;lt;/g, '&amp;amp;lt;')
+ .replace(/&amp;gt;/g, '&amp;amp;gt;')
+ .replace(/\"/g, '&amp;amp;quot;')
+ .replace(/'/g, '&amp;amp;#039;');
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The evidence renderer now uses one shared, long-supported prototype call instead of the newer static method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ const hasOwn = (value, key) =&amp;gt; Object.prototype.hasOwnProperty.call(value, key);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- Object.hasOwn(entry, 'key')
&lt;/span&gt;&lt;span class="gi"&gt;+ hasOwn(entry, 'key')
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The label formatter received the same compatibility treatment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- key.replaceAll('_', ' ')
&lt;/span&gt;&lt;span class="gi"&gt;+ key.replace(/_/g, ' ')
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The regression test applies the compatibility profile before page code runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addInitScript&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hasOwn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;configurable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;replaceAll&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;configurable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/#execution/dev-summer-bug-smash`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;domcontentloaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete patch and reproducible test are retained in the review package. The challenge accepts PRs or code snippets; this submission shows the exact code changes and does not depend on an upstream merge.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Improvements
&lt;/h2&gt;

&lt;p&gt;I kept the correction deliberately small:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no polyfill bundle or compatibility dependency was added;&lt;/li&gt;
&lt;li&gt;the original escaping order remains unchanged;&lt;/li&gt;
&lt;li&gt;property ownership remains safe even if an object shadows &lt;code&gt;hasOwnProperty&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the regression test exercises the real application route and real project state;&lt;/li&gt;
&lt;li&gt;the same test captures full-page evidence before and after the patch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the exact same simulated API profile, the result changed from a blank workspace to a usable execution view:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Workspace rendered&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stage badges&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workspace characters&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;13,922&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compatibility errors&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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%2Fcjns5b8llxm9pzsnxcyj.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%2Fcjns5b8llxm9pzsnxcyj.png" alt="After: the execution workspace renders correctly under the same compatibility profile" width="700" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This does not claim support for Internet Explorer or browsers without ES modules. It closes a narrower, realistic gap: browsers modern enough for the application's architecture but older than &lt;code&gt;Object.hasOwn&lt;/code&gt; or &lt;code&gt;replaceAll&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The durable lesson was to test the user's navigation outcome, not only the helper functions. The regression assertion asks whether the selected execution workspace actually appears. That is the behavior a user depends on.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>PawTwin — A Real-Life Tamagotchi for Families and Their Dogs</title>
      <dc:creator>Andre Gomes</dc:creator>
      <pubDate>Sun, 16 Aug 2026 20:55:21 +0000</pubDate>
      <link>https://dev.to/derivativador/pawtwin-a-real-life-tamagotchi-for-families-and-their-dogs-2k99</link>
      <guid>https://dev.to/derivativador/pawtwin-a-real-life-tamagotchi-for-families-and-their-dogs-2k99</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/weekend-2026-08-13"&gt;DEV Weekend Challenge: Dog Days Edition&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Every family with a dog eventually asks the same question: &lt;strong&gt;did anyone feed the dog?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PawTwin turns that daily uncertainty into a shared game. It is a real-life Tamagotchi whose state is controlled by care performed for the family's physical dog—not a task manager with a mascot pasted on top.&lt;/p&gt;

&lt;p&gt;The family begins with a photo of the real dog. PawTwin creates a reusable pixel identity and places it inside an animated Phaser home. The environment communicates real needs: an empty bowl means food is due, a dry bowl means water is overdue, pacing at the door means a walk is waiting, and dirt particles mean hygiene has slipped. Completing and approving real care makes the twin playful again.&lt;/p&gt;

&lt;p&gt;A caregiver can claim feeding, water, walking, oral-care, ear-care or grooming work. Walks record time, approximate distance and a privacy-reduced route; other tasks can include private photo evidence. A guardian—not computer vision—reviews the proof and decides whether it counts. Approval can release a real Devnet SOL reward through a guardian-signed Solflare transaction.&lt;/p&gt;

&lt;p&gt;The V2 experience also gives the dog a life beyond reminders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pet Memory&lt;/strong&gt; learns explainable patterns such as who normally walks the dog and at what time, then creates personalized in-app reminders and a monthly family report.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find the Ball&lt;/strong&gt; uses a future finalized Solana Devnet blockhash to choose one of four rooms, exposing the slot, blockhash, digest, salted commitment and nonce so anyone can reproduce the zero-stakes result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Life Album&lt;/strong&gt; recreates the thick paper, metal rings, photo corners and protective cellophane of a family photo album, with private photos, stories, places and short recordings of the real pet's original sounds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device Lab&lt;/strong&gt; demonstrates a guarded Raspberry Pi ball-launcher contract while remaining clearly in simulator mode unless safe physical hardware is configured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core loop is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real need → visible game behavior → real family action → proof → guardian approval → reward → happier digital twin → care memory.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The better the family cares for the real dog, the better its digital twin's life becomes.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Live application:&lt;/strong&gt; &lt;a href="https://pawtwin-v2-future.vercel.app" rel="noopener noreferrer"&gt;https://pawtwin-v2-future.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app includes a clearly labelled seeded household so judges can explore the full product even when a camera, wallet or provider is unavailable.&lt;/p&gt;

&lt;p&gt;Suggested judge path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign in with Vercel, or enter the transparent demo.&lt;/li&gt;
&lt;li&gt;Upload a dog photo and reveal the pixel twin.&lt;/li&gt;
&lt;li&gt;Hear the introduction and enter the Phaser room.&lt;/li&gt;
&lt;li&gt;Click the dog, bowls, bed and door; inspect how overdue care changes the world.&lt;/li&gt;
&lt;li&gt;Claim a task, submit evidence and review it as guardian.&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;Play&lt;/strong&gt; and run a live, auditable Find the Ball round against Solana Devnet.&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;Pet Memory&lt;/strong&gt; to inspect learned caregiver routines and generate the monthly report.&lt;/li&gt;
&lt;li&gt;Add a private page to &lt;strong&gt;Life Album&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Connect Solflare to inspect the guardian-signed Devnet reward path.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The public deployment never fabricates provider success. A local twin is labelled &lt;strong&gt;LOCAL FALLBACK&lt;/strong&gt;, practice entropy is labelled &lt;strong&gt;PRACTICE&lt;/strong&gt;, an unsent report says &lt;strong&gt;PREVIEW ONLY&lt;/strong&gt;, and a Solana reward is successful only after a real signature confirms.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Public source and complete Git history:&lt;/strong&gt; &lt;a href="https://pawtwin-v2-future.vercel.app/source" rel="noopener noreferrer"&gt;https://pawtwin-v2-future.vercel.app/source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The source page contains a checksum-protected Git fast-export with the full tracked tree, authors, timestamps, commits, branches and annotated release tags. Restore and verification commands are printed on the page. It is a public Vercel-hosted source mirror—not GitHub—and is labelled accordingly.&lt;/p&gt;

&lt;p&gt;The submitted application revision is commit &lt;code&gt;8ceb68cf3ce3be93e2a82958a7f50040b9de0753&lt;/code&gt; on &lt;code&gt;v2-future&lt;/code&gt;. The final documentation checkpoint is tag &lt;code&gt;hackathon-submission-2026-08-16-final&lt;/code&gt;, and the frozen V1 remains preserved at tag &lt;code&gt;v1.0.0-hackathon&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;PawTwin uses Next.js 16, React 19 and TypeScript for product workflows, while Phaser 4 owns the room, pet movement, interactive objects, particles, tweens and reward celebrations. A deterministic state engine converts task deadlines and approvals into hunger, thirst, walk need, hygiene, energy and happiness; React passes only the derived behavior into the game.&lt;/p&gt;

&lt;h3&gt;
  
  
  Google AI: the face and visual understanding
&lt;/h3&gt;

&lt;p&gt;The server validates and bounds the uploaded image. Gemini 3.5 Flash Lite is integrated for structured visible-trait extraction and conservative proof observations; Gemini 3.1 Flash Image is integrated for a canonical portrait. Results are cached so mood changes never regenerate the dog's identity. Evidence analysis is advisory and the guardian remains authoritative.&lt;/p&gt;

&lt;p&gt;The production request reached Vercel AI Gateway through request-scoped OIDC, but Vercel currently requires a billing card to unlock even its promotional model credits. I did not add a card or mislabel the result: the live demo therefore uses an authored deterministic renderer that samples the uploaded photo's palette and displays &lt;strong&gt;LOCAL FALLBACK&lt;/strong&gt;. The Gemini adapter, schemas, Google-only routing, privacy options and provider-safe failure path are all present in the public source.&lt;/p&gt;

&lt;h3&gt;
  
  
  ElevenLabs: the dog's actual voice
&lt;/h3&gt;

&lt;p&gt;ElevenLabs Flash provides short contextual lines for hunger, water, walks and reward moments. The API key stays server-side, phrases are curated, audio is cached, and the browser receives MP3 bytes. The live deployment produced a verified ElevenLabs TTS request with HTTP 200. If the provider is unavailable, PawTwin explicitly switches to browser speech or visible text rather than calling the fallback ElevenLabs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solana and Solflare: rewards and auditable play
&lt;/h3&gt;

&lt;p&gt;Guardian approval constructs a standard Devnet SOL transfer to the caregiver, opens Solflare through Wallet Adapter, and waits for network confirmation before displaying a reward or Explorer link. There is no server wallet, mainnet key or fabricated signature. Application idempotency blocks a second payout for the same task.&lt;/p&gt;

&lt;p&gt;Find the Ball supplies a second real Solana use: the game commits locally to the hidden room before a selected future slot exists, then resolves with that finalized Devnet blockhash. A live smoke round resolved slot &lt;code&gt;484487750&lt;/code&gt; and exposed the complete reproducible SHA-256 calculation. It is deliberately zero-stakes—not a wager, prize or claim that public blockhashes are secure randomness for valuable outcomes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Snowflake: long-term care memory
&lt;/h3&gt;

&lt;p&gt;Every meaningful mutation creates a privacy-reduced CareEvent. The live production path first wrote &lt;code&gt;PET_CREATED&lt;/code&gt; to the Neon/Postgres operational ledger and returned HTTP 201, then authenticated to Snowflake using a scoped service user and key-pair JWT. Deterministic event IDs and &lt;code&gt;MERGE&lt;/code&gt; make analytical ingestion idempotent.&lt;/p&gt;

&lt;p&gt;Pet Memory turns those facts into visible value: caregiver contribution, usual care time, task patterns, completion streaks, walking distance, rewards, urgent reminders and monthly reporting. Gemini can phrase one bounded narrative over validated aggregates, but it cannot change counts, invent care or see photos, GPS routes, wallet addresses or recipients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resilient, private-by-default product engineering
&lt;/h3&gt;

&lt;p&gt;Photos and reduced GPS evidence stay off-chain. Uploads have signature, type and size validation; server mutations have same-origin checks, session authorization, bounded rate limits and concurrency limits. Private metadata is stripped before Neon or Snowflake. Provider failure degrades the experience without turning failure into fake success.&lt;/p&gt;

&lt;p&gt;The original room and all premium late-VGA/32-bit-style art are authored for PawTwin. Phaser, Next.js, React, Lucide, Solana Wallet Adapter and provider SDKs are used under their published licenses; detailed links and licenses are recorded in &lt;code&gt;docs/ATTRIBUTIONS.md&lt;/code&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Google AI:&lt;/strong&gt; Gemini structured vision, proof understanding and canonical portrait generation are implemented behind validated server routes. The current deployment truthfully shows its deterministic local renderer because AI Gateway's model call is blocked by a billing-card requirement; it does not present local work as Gemini output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Solana:&lt;/strong&gt; Guardian-approved care constructs a real Solflare-signed Devnet SOL reward, and Find the Ball already uses finalized Devnet blocks as a public, reproducible zero-stakes game mechanic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs:&lt;/strong&gt; ElevenLabs Flash is the contextual speaking voice of the digital twin. The live server-side TTS path returned genuine MP3 audio and is distinct from the labelled browser/text fallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Snowflake:&lt;/strong&gt; A scoped JWT service user ingests privacy-reduced, idempotent CareEvents into Snowflake; the live &lt;code&gt;PET_CREATED&lt;/code&gt; event path authenticated successfully and powers the pet's analytical-memory story.&lt;/li&gt;
&lt;/ul&gt;

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