<?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: Subarna Maity</title>
    <description>The latest articles on DEV Community by Subarna Maity (@dronzer2code).</description>
    <link>https://dev.to/dronzer2code</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%2F3868147%2Fd292e2fd-5c3c-44fe-8f45-335aefbd074a.jpeg</url>
      <title>DEV Community: Subarna Maity</title>
      <link>https://dev.to/dronzer2code</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dronzer2code"/>
    <language>en</language>
    <item>
      <title>Repost : Breach Week Dropped 10 'Patch Now' Posts. A Local Gemma Boiled Them Down to 2 Lines, and Code Checked Every Number It Wrote.</title>
      <dc:creator>Subarna Maity</dc:creator>
      <pubDate>Fri, 23 Oct 2026 16:57:38 +0000</pubDate>
      <link>https://dev.to/dronzer2code/breach-week-dropped-10-patch-now-posts-a-local-gemma-boiled-them-down-to-2-lines-and-code-4jf0</link>
      <guid>https://dev.to/dronzer2code/breach-week-dropped-10-patch-now-posts-a-local-gemma-boiled-them-down-to-2-lines-and-code-4jf0</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;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/t-rexbytes/breach-week-dropped-10-patch-now-posts-a-local-gemma-boiled-them-down-to-2-lines-and-code-53n6" class="crayons-story__hidden-navigation-link"&gt;Breach Week Dropped 10 'Patch Now' Posts. A Local Gemma Boiled Them Down to 2 Lines, and Code Checked Every Number It Wrote.&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/t-rexbytes/breach-week-dropped-10-patch-now-posts-a-local-gemma-boiled-them-down-to-2-lines-and-code-53n6" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady Submission 🦥&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/t-rexbytes" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F4026815%2Fa722cacd-1e7c-44a3-9a1d-945c06d48bb4.jpg" alt="t-rexbytes profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/t-rexbytes" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Sourish Panda
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Sourish Panda
                
                
              
              &lt;div id="story-author-preview-content-4906514" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/t-rexbytes" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F4026815%2Fa722cacd-1e7c-44a3-9a1d-945c06d48bb4.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Sourish Panda&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/t-rexbytes/breach-week-dropped-10-patch-now-posts-a-local-gemma-boiled-them-down-to-2-lines-and-code-53n6" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 23&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/t-rexbytes/breach-week-dropped-10-patch-now-posts-a-local-gemma-boiled-them-down-to-2-lines-and-code-53n6" id="article-link-4906514"&gt;
          Breach Week Dropped 10 'Patch Now' Posts. A Local Gemma Boiled Them Down to 2 Lines, and Code Checked Every Number It Wrote.
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hf26challenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hf26challenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/temporal"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;temporal&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/t-rexbytes/breach-week-dropped-10-patch-now-posts-a-local-gemma-boiled-them-down-to-2-lines-and-code-53n6" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;11&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/t-rexbytes/breach-week-dropped-10-patch-now-posts-a-local-gemma-boiled-them-down-to-2-lines-and-code-53n6#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            25 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;---&amp;gt; With &lt;a class="mentioned-user" href="https://dev.to/t-rexbytes"&gt;@t-rexbytes&lt;/a&gt; and &lt;a class="mentioned-user" href="https://dev.to/soumyadeepdey"&gt;@soumyadeepdey&lt;/a&gt; &lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>We Searched eBay for 237 Recalled Products. Only 45 of 12,562 Listings Named the Model.</title>
      <dc:creator>Subarna Maity</dc:creator>
      <pubDate>Sat, 17 Oct 2026 07:26:20 +0000</pubDate>
      <link>https://dev.to/dronzer2code/we-searched-ebay-for-237-recalled-products-only-45-of-12562-listings-named-the-model-2efi</link>
      <guid>https://dev.to/dronzer2code/we-searched-ebay-for-237-recalled-products-only-45-of-12562-listings-named-the-model-2efi</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;Built by &lt;a href="https://dev.to/dronzer2code"&gt;@dronzer2code&lt;/a&gt; and &lt;a href="https://dev.to/soumyadeepdey"&gt;@soumyadeepdey&lt;/a&gt; &lt;a href="https://dev.to/t-rexbytes"&gt;@t-rexbytes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Picture a Thursday afternoon at a kids' consignment shop (a composite, not a real store). A parent drops a bag at the intake desk: a toy fire truck, a wooden learning tower, a sleep machine, some toddler pyjamas. The staffer has about two minutes per item. That morning the U.S. Consumer Product Safety Commission posted its weekly batch of a dozen or so recalls. Nobody at the desk has read them.&lt;/p&gt;

&lt;p&gt;Here's the part of one notice that matters, from September 17, for the Melissa &amp;amp; Doug Lights and Sound Fire Truck (about 84,000 sold at Target):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Affected units will have one arrow pointing to the number 25 and the second arrow pointing to the number 4 or 5 or they will have one arrow pointing to the number 26 and the second arrow pointing to the number 3.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The arrows are two date dials under the battery box. The brand and the price tag can't answer it. You have to turn the toy over.&lt;/p&gt;

&lt;p&gt;For the shop this is a legal question. CPSC's &lt;a href="https://www.cpsc.gov/Newsroom/News-Releases/2009/CPSC-Launches-Resale-Round-up-Campaign-to-Keep-Dangerous-Recalled-Products-Out-of-Resale-Stores-and-Off-the-Internet" rel="noopener noreferrer"&gt;Resale Round-up&lt;/a&gt; campaign told thrift stores and online resellers directly that re-selling recalled products violates federal law. On October 6, &lt;a href="https://www.news4jax.com/money/2026/10/06/consumer-reports-warns-parents-about-unsafe-baby-gear-being-sold-on-craigslist-ebay-facebook-marketplace-other-sites/" rel="noopener noreferrer"&gt;Consumer Reports, via News4Jax&lt;/a&gt;, warned that banned and recalled baby gear is still listed on Craigslist, eBay and Facebook Marketplace. eBay said its product-safety systems (AI models, keyword rules and manual review) stopped 21 million potentially unsafe listings worldwide in 2025. And a &lt;a href="https://www.buchalter.com/blogs/cpsc-escalates-enforcement-in-2026-expanded-market-oversight-civil-penalties-and-criminal-exposure/" rel="noopener noreferrer"&gt;2026 Buchalter analysis&lt;/a&gt; notes that CPSC has now sent its 100,000th takedown notice for recalled or banned products sold online.&lt;/p&gt;

&lt;p&gt;We built &lt;strong&gt;RecallDesk&lt;/strong&gt; for that desk. Every Thursday it reads the new recalls, checks a shop's stock and eBay store against them, and prints one page: what to pull, and what to flip over.&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%2Fd9fehf6pj4323lgogei2.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%2Fd9fehf6pj4323lgogei2.png" alt="RecallDesk printable card for Little Loop Kids Consignment, week of Thursday October 15, 2026, made by the fine-tuned 4B: 6 new CPSC recalls, 60 items checked. A red DO NOT SELL band lists 8 items with CPSC number, reason and remedy: Papablic bedside sleeper (PAPA03001AI, 27027), Primark 101 Dalmatians coin purse (2595301), ONTHEWEI bath seat (MY002), Skip Hop Elmo teether (9R263210), Taleco Gear swing (swing-1), Mangohood kitchen tower (LT003), Love To Dream sleep machine (LTD-SM23) and Romorgniz 6-drawer dresser. An amber CHECK LABEL band lists 6 items with where to look, e.g. Woodure learning tower: the model number is engraved on the underside of the bottom step. At the bottom, two shop corrections applied from Backboard memory: the HARPPA tower and the Melissa &amp;amp; Doug fire truck" width="800" height="1676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the model number hides
&lt;/h2&gt;

&lt;p&gt;Before any model code, we pulled every recall CPSC posted from July 18 to October 15: 151, all on Thursdays, 48 for children's products. Adding children's recalls back to 2023 (shops sell old baby gear too) made 591.&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%2F4i737g80d7gnqt5no2zx.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%2F4i737g80d7gnqt5no2zx.png" alt="Stacked bar chart of CPSC recalls per Thursday from Jul 23 to Oct 15, 2026: 7, 15, 17, 18, 7, 11, 13, 12, 14, 11, 12, 8 and 6 recalls, 151 in total, about 12 a week. Children's products are in blue (48 of 151, 32%), from 0 on Aug 20 to 7 on Jul 30 and Aug 6. Every recall in the period was posted on a Thursday" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, with plain code and no model, we asked where the identifying number appears. In &lt;strong&gt;73 of the 151&lt;/strong&gt; notices, the model, item, style or SKU number is only in the description, never in the headline or product name (24 of 48 for children's). "Woodure Toddler Kitchen Step Stools Recalled" gives you the brand. Whether &lt;em&gt;your&lt;/em&gt; stool is affected is further down: "The model number is engraved on the underside of the bottom step."&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%2Fuq1bzxd1ycsmf800lkda.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%2Fuq1bzxd1ycsmf800lkda.png" alt="100% stacked bar chart of the 151 CPSC recalls since Jul 18, 2026: 73 (48%) have a model, item, style, SKU or part number only in the description, 11 also show it in the title or product name, and 67 have no model-type number. Children's recalls (48): 24 description only, 3 in the title, 21 none. Using the teacher model's extracted model numbers instead of regexes gives 88 description-only" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the resale side, SerpApi's eBay engine searched for 237 recalls (the 151 recent ones plus 86 older children's ones; 235 paid searches, since two recalls shared a cached query): 14,068 results, &lt;strong&gt;12,562 unique listings&lt;/strong&gt;. We counted titles containing a recalled identifier, ignoring case and dashes. There were &lt;strong&gt;45&lt;/strong&gt;, across 13 recalls. For children's recalls, &lt;strong&gt;2 of 6,901&lt;/strong&gt;, both for one brand of pool dive sticks.&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%2F1qoy044gfcijmkywg27o.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%2F1qoy044gfcijmkywg27o.png" alt="Two panels. Left: a histogram of unique eBay listings per recall search across 237 searches (151 recalls since Jul 18 plus 86 older children's recalls): 210 returned a full page of 50 or more, 7 returned 25-49, 7 returned 10-24 and 13 returned 1-9. Right: a funnel from 237 recalls searched, to 150 with a model number or UPC in their spec, to 13 with at least one listing whose title contains a recalled identifier exactly (45 listings, deterministic string match)" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Second-hand sellers are the least likely to type one: &lt;strong&gt;17.1%&lt;/strong&gt; of all listings were pre-owned (&lt;strong&gt;20.2%&lt;/strong&gt; for children's recalls), but only 9% of the 45 exact hits were.&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%2Fqb3gxditqbyyuk1q4o9b.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%2Fqb3gxditqbyyuk1q4o9b.png" alt="100% stacked bars of eBay listing condition. All 12,562 unique listings: 78% new, about 3% open box, 17% pre-owned. Listings returned for children's recalls (6,901): 79% new, 20% pre-owned. The 45 listings whose title names a recalled identifier: 78% new, 13% open box, 9% pre-owned" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That count shaped the product. A keyword filter can flag the 45 but can't clear the other 12,517, because a title without a model number proves nothing. For most same-brand items the useful answer is "go look at the label", plus where the label is.&lt;/p&gt;

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

&lt;p&gt;RecallDesk is a weekly recall desk for resale businesses. A shop loads its stock once (CSV or paste) and, optionally, its eBay seller name. Every Thursday at 2:00 PM Eastern, after CPSC posts, it turns each new recall into a structured spec (brand, model numbers, date codes, sold window), compares every item and live listing against the specs that could apply, applies the shop's past corrections, and prints one card: &lt;strong&gt;DO NOT SELL&lt;/strong&gt; for &lt;code&gt;match&lt;/code&gt;, &lt;strong&gt;CHECK LABEL&lt;/strong&gt; for &lt;code&gt;check&lt;/code&gt; with the place to look, and nothing for &lt;code&gt;no_match&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A trimmed excerpt of a real card for the demo shop, from a run with the fine-tuned model on Tinker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=== RECALL CARD card_2026-10-15_little-loop  Little Loop Kids Consignment  (week of 2026-10-15) ===
  6 new recalls this week | 60 items checked | model: qwen3.5-4b-recalldesk-lora
  DO NOT SELL (8)
    x Papablic Convertible Bedside Sleeper with Play Yard (model PAPA03001AI)  -&amp;gt; CPSC 27027
        why: Listing matches recalled brand, product type, and explicitly states the recalled model number.
        remedy: Stop use, destroy product, email photos for refund
    x Love To Dream Portable Sleep Machine with Night Light (model LTD-SM23)  -&amp;gt; CPSC 26798
  CHECK LABEL (7)
    ? Woodure Wooden Toddler Learning Tower  -&amp;gt; CPSC 26658
        look for: model no. WD1764, WD1357, WD1720; sold 2024-07 to 2026-06. The model number is
                  engraved on the underside of the bottom step.
    ? Sweet Cubby Kids Loungewear Set (shirt + shorts)  -&amp;gt; CPSC 26752
        look for: lot/date code Batch No.: 272779; sold 2025-06 to 2026-05. "Batch No.: 272779" and
                  the size are printed on the sewn-in neck label and on the seam label.
  SHOP MEMORIES APPLIED (1)
    * beeeda90-bb74-4681-b1fa-bfdacd9ef8e6: Shop correction: 'HARPPA Nordi Foldable Toddler Tower' is
      NOT the recalled product for CPSC recall 26651. Ours is the 2022 one - consignor's receipt s...
  model usage: 179 classify calls for 60 items | ... | est. $0.0348 at Tinker list price
  workflow: check-little-loop-2026-10-17-e610   memory: backboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;One line on that card is wrong, and it's wrong in the cautious direction. The Love To Dream recall covers model LTD-SM23 &lt;em&gt;with&lt;/em&gt; date code 7W15TN, so our own rule 3 says the item should be CHECK LABEL, not DO NOT SELL. The shop would pull a sleep machine that might be fine.&lt;/p&gt;

&lt;p&gt;The dashboard is Next.js with &lt;a href="https://boardui.com" rel="noopener noreferrer"&gt;BoardUI&lt;/a&gt; components. Most owners only need the printable card. The rest is for setup and for answering "why was this flagged?"&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%2F10qxeha62crc5s5elcge.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%2F10qxeha62crc5s5elcge.png" alt="RecallDesk overview: a KPI row (new recalls this Thursday, items checked, do-not-sell and check-label counts), this week's card as a sortable table, a countdown to the next scheduled sweep, the recalls posted this Thursday, an eBay summary and an activity feed" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any inventory row opens beside the recall it was compared with. &lt;strong&gt;"Not a match? Correct this"&lt;/strong&gt; is how a shop teaches the system about its own stock.&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%2F2r7z11pkudzgakd6q5ls.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%2F2r7z11pkudzgakd6q5ls.png" alt="Inventory page with an item drawer open: the shop's item on one side, the matched CPSC recall and its extracted spec on the other, a verdict pill, and the " width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The recalls page puts CPSC's text, identifiers highlighted, next to the extracted spec:&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%2Fae6xue2q7kvebu61opfb.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%2Fae6xue2q7kvebu61opfb.png" alt="Recall page with CPSC 26658, Woodure Toddler Kitchen Step Stools (about 91,000 units, sold on Amazon), expanded: the original notice with models WD1764, WD1357 and WD1720 highlighted, including " width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The eBay monitor is for flippers who sell online:&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%2Funaals74pssjwsev66c8.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%2Funaals74pssjwsev66c8.png" alt="eBay monitor: searches spent, listings seen and flagged counts, and per-recall rows that expand into listing cards with title, condition, price and verdict; sellers appear only as a hashed id" width="800" height="1016"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who would pay?&lt;/strong&gt; Kids' consignment shops, which take in second-hand children's goods from strangers daily. Seasonal consignment events, with thousands of items and volunteer staff in one weekend. eBay and Poshmark flippers buying returns pallets blind. We priced it at &lt;strong&gt;$15 a month per shop&lt;/strong&gt; and &lt;strong&gt;$49 per consignment event&lt;/strong&gt;. A full re-check of the 60-item demo shop took 179 model calls and cost $0.035.&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%2F9vdxgkf7js3yn5caoou5.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%2F9vdxgkf7js3yn5caoou5.png" alt="Pricing page: Shop plan at $15 per month per shop (weekly card every Thursday, unlimited inventory checks, eBay store re-check, shop memory) and Consignment event at $49 per event (check every consignor's intake list, one card per drop-off batch, printable pull list, recalls back to 2023)" width="800" height="751"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It isn't legal advice or a substitute for &lt;a href="https://www.cpsc.gov/Recalls" rel="noopener noreferrer"&gt;cpsc.gov/Recalls&lt;/a&gt;. It reads text and can't see a label in a photo, which is why CHECK LABEL exists. U.S. CPSC recalls only.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;One-click deploy of the full stack (agent runtime plus dashboard) from the repo's Render Blueprint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://render.com/deploy?repo=https://github.com/SoumyaEXE/recalldesk" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frender.com%2Fimages%2Fdeploy-to-render-button.svg" alt="Deploy to Render" width="153" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or run it locally. Every key is optional, and anything missing falls back to a labelled substitute.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/SoumyaEXE/recalldesk &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;recalldesk
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env            &lt;span class="c"&gt;# TINKER_API_KEY / SERPAPI_API_KEY / BACKBOARD_API_KEY, all optional&lt;/span&gt;

python &lt;span class="nt"&gt;-m&lt;/span&gt; uv venv &lt;span class="nt"&gt;--python&lt;/span&gt; 3.12 agent/.venv
python &lt;span class="nt"&gt;-m&lt;/span&gt; uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--python&lt;/span&gt; agent/.venv/Scripts/python.exe &lt;span class="nt"&gt;-r&lt;/span&gt; agent/requirements-tinker.txt
agent/.venv/Scripts/python &lt;span class="nt"&gt;-m&lt;/span&gt; agent.dev &lt;span class="nt"&gt;--schedule&lt;/span&gt;        &lt;span class="c"&gt;# Temporal :7233 (UI :8233), worker, API :8000&lt;/span&gt;
agent/.venv/Scripts/python &lt;span class="nt"&gt;-m&lt;/span&gt; agent.run_check little-loop &lt;span class="c"&gt;# run one shop's check and print its card&lt;/span&gt;
agent/.venv/Scripts/python &lt;span class="nt"&gt;-m&lt;/span&gt; agent.demo_crash            &lt;span class="c"&gt;# kill the worker mid-check, watch it recover&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;web &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev                      &lt;span class="c"&gt;# dashboard on :3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;On macOS or Linux the interpreter is &lt;code&gt;agent/.venv/bin/python&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/SoumyaEXE" rel="noopener noreferrer"&gt;
        SoumyaEXE
      &lt;/a&gt; / &lt;a href="https://github.com/SoumyaEXE/recalldesk" rel="noopener noreferrer"&gt;
        recalldesk
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Before you list it, check it. Weekly CPSC recall check for resellers and kids' consignment shops — a 4B open model fine-tuned on Tinker, Temporal, SerpApi, Backboard.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;RecallDesk&lt;/h1&gt;
&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Before you list it, check it.&lt;/strong&gt; A weekly CPSC recall check for kids' consignment shops and eBay resellers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/SoumyaEXE/recalldesk/docs/images/cover.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FSoumyaEXE%2Frecalldesk%2FHEAD%2Fdocs%2Fimages%2Fcover.png" alt="RecallDesk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reselling a recalled children's product is a federal violation in the US, and recalled baby gear keeps turning up on
resale sites. CPSC posts about a dozen recalls every Thursday, and in &lt;strong&gt;73 of the 151 recalls since 2026-07-18 the model
number that decides it is only in the description's fine print&lt;/strong&gt;. eBay sellers almost never type it into a title.&lt;/p&gt;
&lt;p&gt;RecallDesk reads every recall, checks a shop's inventory and eBay listings against it, and prints &lt;strong&gt;one card a week&lt;/strong&gt;
&lt;strong&gt;DO NOT SELL&lt;/strong&gt; for items that are the recalled product, &lt;strong&gt;CHECK LABEL&lt;/strong&gt; (with exactly where the label is) for items
only a person holding it can decide. Then it goes quiet until next Thursday.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The rule of the build: the big model teaches, the small model reads, code decides.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/SoumyaEXE/recalldesk" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;There are four directories. &lt;code&gt;ml/&lt;/code&gt; holds data collection, specs, eBay search, labelling, training and evaluation. &lt;code&gt;agent/&lt;/code&gt; holds the Temporal workflows, the API and shop memory. &lt;code&gt;web/&lt;/code&gt; is the dashboard. &lt;code&gt;shared/schema.py&lt;/code&gt; is the single contract the other three code against. Every figure here traces to &lt;code&gt;ml/results/*.json&lt;/code&gt;, &lt;code&gt;data/*/…stats.json&lt;/code&gt;, &lt;code&gt;docs/charts/chart_data.json&lt;/code&gt; or a transcript under &lt;code&gt;agent/demo_logs/&lt;/code&gt;. License: MIT.&lt;/p&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%2Fnu90avllp9xc9td0yczt.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%2Fnu90avllp9xc9td0yczt.png" alt="Architecture diagram. Offline, once: CPSC recalls go to the Qwen3.5-397B teacher on Tinker, which writes a spec per recall; SerpApi searches eBay for each; code builds (recall, listing) pairs split by recall; the teacher labels every pair; code enforces rule 7 on the training targets; a rank-32 LoRA on Qwen3.5-4B is trained on Tinker and scored on 20 held-out recalls. Weekly: a Temporal schedule on Thursdays at 14:00 Eastern runs fetch_new_recalls, extract_spec, load_shop_memories (Backboard), check_item per item with the LoRA, scan_ebay_store (SerpApi), compose_card and publish_card, and shop corrections flow back into Backboard" width="800" height="813"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Rules first, model second
&lt;/h3&gt;

&lt;p&gt;We wrote the decision rules before choosing a model. Our three verdicts are defined in &lt;a href="https://github.com/SoumyaEXE/recalldesk/blob/main/ml/LABELING.md" rel="noopener noreferrer"&gt;&lt;code&gt;ml/LABELING.md&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;match:&lt;/strong&gt; the item is the recalled product. Brand and line agree, and either a recalled identifier is visible or the recall covers every unit of that exact product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;check:&lt;/strong&gt; same brand and line, but whatever decides it (model number, date code, sold window, variant) isn't visible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;no_match:&lt;/strong&gt; a different product, an excluded model, or only an accessory, part or "compatible with" item.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seven ordered rules apply them. Rule 7 did the most work: &lt;em&gt;an unbranded listing with no recalled identifier is a generic look-alike, so no_match&lt;/em&gt;, unless it names something only the recalled product has, like a licensed character. The model outputs one small JSON object, and plain code maps it to the card.&lt;/p&gt;

&lt;p&gt;A large open model, &lt;strong&gt;Qwen3.5-397B-A17B&lt;/strong&gt;, wrote specs for all 591 recalls (100 seconds, $2.21), and code drops any identifier not actually in the notice. It also labelled 3,300 (recall, listing) pairs, and kept calling unbranded look-alikes "check": 1,134 checks on the first pass, 872 after we added rule 7 to the prompt. Still too many, so we stopped asking. A short function checks the rule and rewrites the &lt;em&gt;training target&lt;/em&gt; when the teacher breaks it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_generic_lookalike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;brand_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;brand&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="c1"&gt;# names the brand -&amp;gt; not generic
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;id_hit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;                              &lt;span class="c1"&gt;# shows a recalled identifier -&amp;gt; not generic
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;distinctive_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;      &lt;span class="c1"&gt;# fewer than 2 distinctive product-name words
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It fired on 391 pairs: 357 in training and 34 in the test split. Every change is logged in &lt;code&gt;data/labeled/targets.jsonl&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 397B teaches once; a 4B works every Thursday
&lt;/h3&gt;

&lt;p&gt;At &lt;strong&gt;$2.20 per 1,000 checks&lt;/strong&gt;, the 397B is too expensive to run for every shop every week. So with Tinker we fine-tuned &lt;strong&gt;Qwen3.5-4B&lt;/strong&gt; (LoRA rank 32) on its corrected labels: 3,007 pairs, 2 epochs fixed in advance, 376 steps at batch 16, learning rate 4.91e-4 decaying linearly, loss on the JSON only. Our code assembles batches and calls &lt;code&gt;forward_backward&lt;/code&gt; and &lt;code&gt;optim_step&lt;/code&gt;; Tinker's GPUs do the rest, and &lt;code&gt;save_weights_for_sampler&lt;/code&gt; returns the &lt;code&gt;tinker://&lt;/code&gt; path the weekly workflow samples. About 38 minutes; held-out loss 0.724 → 0.228. With a ~400-token prompt (the teacher's is ~1,060), the student costs &lt;strong&gt;$0.19 per 1,000 checks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Tinker spend at list prices: specs $2.21, discarded first labelling run $6.80, second run $7.47, training $2.39, evaluation $0.23, &lt;strong&gt;about $19.10&lt;/strong&gt; total. SerpApi: 235 of the free plan's 250 searches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scoring it without fooling ourselves
&lt;/h3&gt;

&lt;p&gt;We split by &lt;strong&gt;recall&lt;/strong&gt;: 217 for training, 20 held out (12 children's), with no recall or listing shared. One try per pair at temperature 0; invalid JSON is wrong. The evaluation harness (bootstrap CIs, exact McNemar) and the Temporal crash-test pattern come from &lt;a class="mentioned-user" href="https://dev.to/soumyadeepdey"&gt;@soumyadeepdey&lt;/a&gt;'s earlier open-source TrailTruth project (&lt;a href="https://github.com/SoumyaEXE/trailtruth" rel="noopener noreferrer"&gt;repo&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One caveat shapes everything below.&lt;/strong&gt; We built a blind labelling tool but didn't finish human labels in time, so the answer key for the 293 test pairs is &lt;strong&gt;the teacher's verdict with rule 7 applied by code&lt;/strong&gt;, not a person's. The scores show how faithfully a 4B learned the teacher's rulebook on unseen recalls, not how often it's right about the world.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;On 293 held-out pairs&lt;/th&gt;
&lt;th&gt;Accuracy (95% CI)&lt;/th&gt;
&lt;th&gt;False alarms (of 255)&lt;/th&gt;
&lt;th&gt;Risky pairs flagged (of 38)&lt;/th&gt;
&lt;th&gt;$ / 1k checks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.5-4B, base&lt;/td&gt;
&lt;td&gt;0.512 (0.457–0.570)&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;$0.22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.5-4B, base + teacher's full rulebook&lt;/td&gt;
&lt;td&gt;0.625 (0.570–0.679)&lt;/td&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;$0.37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identifier/keyword rules, no model&lt;/td&gt;
&lt;td&gt;0.864 (0.823–0.901)&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Teacher, Qwen3.5-397B-A17B&lt;/td&gt;
&lt;td&gt;0.884 (0.846–0.918)&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;$2.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Qwen3.5-4B + RecallDesk LoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.918 (0.884–0.949)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;12&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;26&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.19&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The teacher's 38 of 38 is automatic, because every gold "check" is one of its own. Its 34 false alarms are exactly the pairs rule 7 overrode. The base 4B returned invalid JSON on 14 pairs, and every other model on none.&lt;/p&gt;

&lt;p&gt;Fine-tuning moved the 4B from 0.512 to 0.918 (+40.6 points, 95% CI +33.8 to +47.1, exact McNemar p &amp;lt; 0.001). Against the teacher the gap (+3.4, p = 0.20) isn't significant: same reading, about a twelfth of the cost. For a shop the false-alarm column matters most. The base model sent 120 of 255 harmless listings to CHECK LABEL, which would bury the real ones. The LoRA sends 12.&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%2Fh59nsqc42wcj7ys3tn8d.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%2Fh59nsqc42wcj7ys3tn8d.png" alt="Bar chart of accuracy on 293 held-out pairs from 20 unseen recalls, scored against teacher labels with rule 7 enforced by code: LoRA 0.918 (95% CI 0.884-0.949), teacher 0.884, identifier rules 0.864, base + rulebook 0.625, base 0.512, with cost per 1,000 checks beside each bar" width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The price: the LoRA flags &lt;strong&gt;26 of the 38 risky pairs&lt;/strong&gt;; its base flagged 34 (on risky pairs alone the base is ahead, 11 to 3, p = 0.057, not significant). Nine of the twelve misses are one recall, a Sviyatp pool drain cover that, per CPSC, "has no labeling or product identification on the product itself". The listings are unbranded white drain covers, which rule 7 reads as no_match, and that's what the student said. Our coded rule kept them as "check" only because the titles share "dual suction outlet" with the product name printed on Sviyatp's packaging. Two more misses are unbranded baby toys; one is a 14-pack of finger lights against a recall of 28- and 30-packs. So it's a genuine trade: fewer false alarms in exchange for trusting rule 7 on products with no label at all. On the 180 children's pairs the LoRA scores 0.933, the base 0.428.&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%2Fquritzxleah2ug3xcran.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%2Fquritzxleah2ug3xcran.png" alt="Unit chart of the 38 held-out risky pairs: the base 4B flagged 34 and missed 4 (3 cleared, 1 invalid JSON); the LoRA flagged 26 and cleared 12, nine of them listings for the unlabeled Sviyatp pool drain cover" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A concrete false alarm: for TheKiddoSpace's recalled LED hover ball, eBay returned "Rechargeable Hover Soccer Ball LED Lights Air Power Indoor Outdoor Toy Kids 3+", no brand anywhere. The base said &lt;code&gt;check&lt;/code&gt; because the listing "matches the brand". The LoRA said &lt;code&gt;no_match&lt;/code&gt;: "Generic look-alike: no brand and no recalled identifier in the listing." Per-request latency on Tinker's hosted sampler is 2.3–2.8 s for every model, so the win is cost, not speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding the listings (SerpApi)
&lt;/h3&gt;

&lt;p&gt;Each call is &lt;code&gt;engine=ebay&lt;/code&gt;, &lt;code&gt;ebay_domain=ebay.com&lt;/code&gt;, &lt;code&gt;_ipg=50&lt;/code&gt;, with the teacher's first query as &lt;code&gt;_nkw&lt;/code&gt;. Responses are cached under a hash of their parameters, so nothing is paid for twice, and a 429 halts the run. A shop's own store uses &lt;code&gt;_sasl=&amp;lt;seller&amp;gt;&amp;amp;_saslop=1&lt;/code&gt; (the engine has no &lt;code&gt;_ssn&lt;/code&gt;). Seller names stay in the raw cache; published data uses a hash.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thursday, every Thursday (Temporal)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;recalldesk-weekly-sweep&lt;/code&gt; is a Temporal Schedule for Thursdays, 14:00 America/New_York. It next fires October 22 and 29 at 18:00 UTC, then November 5 at 19:00 UTC once the clocks change. Every step is an activity with its own failure policy: CPSC fetches back off, then fall back to a committed snapshot; classification heartbeats with six calls in flight, so a stuck call gets retried; malformed JSON is retried, then answered by the rules with a note on the card; a SerpApi 429 is never retried, and the step records &lt;code&gt;source=snapshot (SerpApi quota/429, not retried)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Does a dead worker lose work? We started a check of the demo shop's 60 items (176 comparisons) and killed the worker process 12.4 seconds in, 19 items done. For three seconds nothing ran, while Temporal still showed the workflow running with six activities in flight. A fresh worker replayed the history and published the card at 33.2 seconds. The history: 60 item activities, each scheduled once; the 20 finished before the kill were kept, the 6 in flight got a second attempt, none completed twice. This rehearsal used the rules classifier plus 1.2 s of artificial latency per item, to make sure the kill hit mid-run.&lt;/p&gt;

&lt;p&gt;Then we did it again with the real LoRA on Tinker (workflow &lt;code&gt;check-little-loop-2026-10-17-1a16-crashdemo&lt;/code&gt;). We killed worker pid 12764 at 73.2 seconds with 18 of 60 items done and 6 calls in flight, and a fresh worker started at 76.5 seconds. The card was published at 170.9 seconds. Again there were 60 activities, each scheduled once: 18 kept, 6 retried, none completed twice, 344 history events. The chart below is this LoRA run; the Temporal UI screenshot after it is from the rehearsal.&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%2Fq3hz2fh48ythfusumyri.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%2Fq3hz2fh48ythfusumyri.png" alt="Timeline of the crash run on the real LoRA: worker 1 (pid 12764) hard-killed at t+73.2 s with 18 of 60 items done and 6 activities in flight; three seconds with no worker while Temporal still shows the workflow RUNNING; worker 2 (pid 17928) starts at t+76.5 s and replays the history; 18 finished activities kept, 6 orphaned ones retried, none completed twice; card published at t+170.9 s; 344 history events" width="800" height="859"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk3ugdjxitio02u9e5cx1.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%2Fk3ugdjxitio02u9e5cx1.png" alt="Temporal Web UI timeline of workflow check-little-loop-2026-10-17-7a6f-crashdemo, completed, 392 events: one bar per check_item activity, the retried ones spanning the gap with no live worker, then scan_ebay_store, compose_card and publish_card" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dashboard's runs page badges the same run as resumed:&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%2Fkxftlm19i6pg81cm37lp.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%2Fkxftlm19i6pg81cm37lp.png" alt="RecallDesk runs page with the crash-test run selected: completed, 7 activities, 392 history events, handled by worker 16408 and then worker 20396, with the check_items step marked as resumed after a worker crash on attempt 2" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With injected faults on the LoRA worker (fake CPSC 503s, hung calls, broken JSON), ten items needed retries and three were answered by the labelled fallback, and the card noted it. A manual sweep for recalls since October 8 fetched 14 live from CPSC and fanned out to one child workflow per shop, producing two cards in 83 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remembering a shop's corrections (Backboard)
&lt;/h3&gt;

&lt;p&gt;No model can know that this shop's fire truck reads 24/11 on its dials. The staffer holding it can. Their "Not a match? Correct this" note is stored as a memory in the shop's own Backboard assistant (explicit memory endpoints only). Next run, &lt;code&gt;load_shop_memories&lt;/code&gt; retrieves it by meaning, not exact text. The memory is kept away from the model, which still answers &lt;code&gt;check&lt;/code&gt;; code then changes the verdict to &lt;code&gt;no_match&lt;/code&gt; and lists the correction on the card. In the recorded run (LoRA on Tinker, Backboard memory &lt;code&gt;063081d1-…&lt;/code&gt;), CHECK LABEL went from 7 items to 6 and DO NOT SELL stayed at 8.&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%2Faofp65i0fr0k1746yeg9.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%2Faofp65i0fr0k1746yeg9.png" alt="Three panels. Run 1: Melissa &amp;amp; Doug Lights &amp;amp; Sounds Fire Truck Activity Board marked CHECK LABEL for CPSC 26780. Correction: " width="800" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An override needs a matching recall number and at least 60% word overlap with the remembered item, so a note about one tower can't clear another. The same run also applied an older HARPPA tower correction (memory &lt;code&gt;beeeda90-…&lt;/code&gt;). One honest wrinkle: semantic search was flaky on long item names. The full "Melissa &amp;amp; Doug Lights &amp;amp; Sounds Fire Truck Activity Board" query didn't return its own correction, while a shorter brand + name query did. So each run now searches with brand, name and model only, and also lists the shop's memories once, putting both through the same overlap guard. In the recorded run the fire-truck correction came through the list.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A 402 froze training.&lt;/strong&gt; When our Tinker credit ran out mid-run, the pending request never resolved, and the loop waited on it with no error. Every wait now has a 300-second timeout, and weights are saved at the end of each epoch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The teacher wouldn't follow rule 7.&lt;/strong&gt; Two prompt revisions only reduced the violations, so in the end code enforced the rule on the training targets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backboard's free credit covered memory only.&lt;/strong&gt; We had planned to run extra open-weight baselines through Backboard's chat, and that wasn't included, so those rows aren't in the table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entire recorded nothing.&lt;/strong&gt; Its hooks are installed per repository, and our coordinating session was started outside the repo folder, so no checkpoints exist and we didn't enter that category.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The first labelling run crashed on its first write&lt;/strong&gt; because the output folder didn't exist yet. That took one line to fix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some recalls can't be read by anyone.&lt;/strong&gt; Six notices end with "the following model numbers are included:" and put the list only on cpsc.gov, which refuses automated requests.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;A small shop's margins can't carry a frontier-model bill per item per week. Open weights let us train a 4B for this one job, and training is where the false alarms went; a closed API would have left a choice between the expensive model and the noisy prompt.&lt;/p&gt;

&lt;p&gt;No lock-in either: the adapter exports as PEFT weights for a shop's own vLLM or llama.cpp server, selected with one environment variable, and with no keys RecallDesk still runs on its rules classifier and labels its cards accordingly. Specs, splits, predictions and the eval script are in the repo, so anyone can re-score our 293 pairs or add the human answer key we didn't get to.&lt;/p&gt;

&lt;p&gt;Privacy: everything RecallDesk reads is public (CPSC's open API, public eBay search results via SerpApi). Seller usernames never leave the gitignored raw cache, and the demo shops are invented. By default, though, labelling, training and inference run on Tinker's hosted GPUs; local means the exported adapter.&lt;/p&gt;

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

&lt;p&gt;A coordinating session ran five parallel coding agents (ML and data, Temporal backend, dashboard, charts and screenshots, writing). Its first output wasn't code but &lt;a href="https://github.com/SoumyaEXE/recalldesk/blob/main/CONTRACT.md" rel="noopener noreferrer"&gt;&lt;code&gt;CONTRACT.md&lt;/code&gt;&lt;/a&gt;: every data shape, every API route, and the rule that code, not the model, decides what reaches the card. With that pinned, the dashboard was built on real-data fixtures while training was still running, and the parts fit on first connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  My First User
&lt;/h2&gt;

&lt;p&gt;No paying shop has used RecallDesk yet, and we won't dress up a hypothetical as a customer. From the first kids' consignment owner we want to learn how long recall checks take today, whether CHECK LABEL is clear without a walkthrough, what price they'd name before seeing ours, and what would stop them. We suspect liability and data entry, which would put a POS import ahead of any model work.&lt;/p&gt;

&lt;p&gt;So here's the offer, open until winners are announced: if you run a resale shop or flip on eBay in the U.S., send us your item list or seller name and we'll run it through RecallDesk this Thursday and send you the card, free. We'll add the first real reply to this section, with permission.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Tinker:&lt;/strong&gt; Qwen3.5-397B-A17B writes specs and labels; Tinker's training API fine-tunes Qwen3.5-4B (LoRA rank 32), and its sampler serves it every Thursday. On 20 unseen recalls: 0.512 → 0.918 accuracy (p &amp;lt; 0.001), 120 → 12 false alarms of 255, $0.19 vs $2.20 per 1,000 checks, about $19.10 all in. Caveats (teacher-built answer key, 26 of 38 risky flagged) are spelled out above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of SerpApi:&lt;/strong&gt; 235 cached eBay-engine searches across 237 recalls, 12,562 unique listings, and the 45-of-12,562 finding the product is built around; plus a weekly per-store scan via &lt;code&gt;_sasl&lt;/code&gt; + &lt;code&gt;_saslop=1&lt;/code&gt;, with 429s never retried.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Temporal:&lt;/strong&gt; a DST-aware Thursday Schedule, per-activity retry and fallback policies, heartbeats on classification, and a worker-kill test: 60 activities, 20 kept, 6 retried, zero duplicates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Backboard:&lt;/strong&gt; per-shop correction memories, retrieved by meaning and applied by code behind a recall-number and word-overlap guard, so one line from a staffer changes next week's card.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you run a kids' consignment shop or a seasonal sale, or you flip returns on eBay, comment or message us. We'll run your store through RecallDesk free this Thursday and send you the card. For everyone else: has a recall ever made you turn something over to read the label, and what was it?&lt;/p&gt;

&lt;p&gt;Built by &lt;a href="https://dev.to/dronzer2code"&gt;@dronzer2code&lt;/a&gt; and &lt;a href="https://dev.to/soumyadeepdey"&gt;@soumyadeepdey&lt;/a&gt; &lt;a href="https://dev.to/t-rexbytes"&gt;@t-rexbytes&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>FarbeDex: a colour picker that only works outside (and never needs the internet)</title>
      <dc:creator>Subarna Maity</dc:creator>
      <pubDate>Sun, 11 Oct 2026 05:35:12 +0000</pubDate>
      <link>https://dev.to/dronzer2code/farbedex-a-colour-picker-that-only-works-outside-and-never-needs-the-internet-4b9o</link>
      <guid>https://dev.to/dronzer2code/farbedex-a-colour-picker-that-only-works-outside-and-never-needs-the-internet-4b9o</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I pick colours for a living, and almost all of them come from other screens: palette generators, screenshots, a hex code a client pasted into chat. Meanwhile the best palettes I've ever seen were outside my window the whole time. Moss on a wet wall. A late sky fading from peach to blue. Rust on a gate nobody has painted in years.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;FarbeDex&lt;/strong&gt;, an Android app that turns what you see outside into colours you can keep.&lt;/p&gt;

&lt;p&gt;It takes almost no time. Point your camera at the view, the sky, a cloud, even the clouds below you from a plane window, and tap once. The palette appears right away, with how much of the scene each colour covers. Pick your two favourites, Gemma names them a moment later, and your phone goes back in your pocket. You go back to the view.&lt;/p&gt;

&lt;p&gt;Your favourites join a collection of 151 colours, from everyday greys to Mythical vivid violets, each one a little &lt;strong&gt;colour pal&lt;/strong&gt; with its own face. And every catch hands you a palette you can drop straight into your next design.&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%2Fo4z2enjbe34nbfo4v25n.jpeg" 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%2Fo4z2enjbe34nbfo4v25n.jpeg" alt="Orboarding - colro lives outside" width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most apps that get you outside give you a job: run this far, find this bird, finish this quest. FarbeDex gives you none. No checklist, no target, no streak. When something makes you stop and look, that's your catch.&lt;/p&gt;

&lt;p&gt;And it all runs &lt;strong&gt;completely offline&lt;/strong&gt;, with two open AI models living on your phone.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Home: your collection at a glance&lt;/strong&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%2F1hi9nbohj2yd2hponabm.jpeg" 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%2F1hi9nbohj2yd2hponabm.jpeg" alt="Dashboard" width="720" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catch, then pick your two favourites&lt;/strong&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%2Faxwuxrqdjew2a2tl1e3f.jpeg" 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%2Faxwuxrqdjew2a2tl1e3f.jpeg" alt="Top colors found" width="720" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Legendary reveal&lt;/strong&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%2Fgf5fvv32xh4ic5e0fv37.jpeg" 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%2Fgf5fvv32xh4ic5e0fv37.jpeg" alt="Legendary Color Captured" width="720" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your FarbeDex fills up&lt;/strong&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%2Fh8oji10p2hgq1kubd9pg.jpeg" 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%2Fh8oji10p2hgq1kubd9pg.jpeg" alt="FarbeDex View of Captured colors" width="720" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemma naming colours in airplane mode&lt;/strong&gt;&lt;br&gt;
🎬 Demo video:   &lt;iframe src="https://www.youtube.com/embed/SuoTWq1lB0s" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;br&gt;
📱 APK: &lt;a href="https://github.com/Dronzer2code/FarbeDex/releases/tag/v4.0" rel="noopener noreferrer"&gt;https://github.com/Dronzer2code/FarbeDex/releases/tag/v4.0&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Dronzer2code" rel="noopener noreferrer"&gt;
        Dronzer2code
      &lt;/a&gt; / &lt;a href="https://github.com/Dronzer2code/FarbeDex" rel="noopener noreferrer"&gt;
        FarbeDex
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;FarbeDex&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Catch real colours from real places.&lt;/strong&gt; FarbeDex is an offline Android app for designers and front-end developers. You go outside, photograph a scene with the in-app camera, and FarbeDex extracts its palette and how much of the scene each colour covers. Every colour lands in the FarbeDex: a fixed index of 151 colour entries (#001–#151) that you complete over time.&lt;/p&gt;
&lt;p&gt;Built for the DEV Hacktoberfest Open-Source AI Challenge, Week 1: Touch Grass.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Screenshots and demo video: &lt;em&gt;TODO (added after the field test)&lt;/em&gt;
APK download: &lt;em&gt;TODO (GitHub release v1.0.0)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Camera only.&lt;/strong&gt; There is no way to import a picture. Live checks (hand-held motion, a fresh capture) and the Referee make sure a catch is a real outdoor scene, not a screen or a print.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic colour maths.&lt;/strong&gt; The photo is reduced to up to 7 colours with k-means in OKLab, and each colour's share of the scene…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Dronzer2code/FarbeDex" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;h3&gt;
  
  
  The maths sees, the models judge and name
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;The biggest decision was what &lt;em&gt;not&lt;/em&gt; to give to AI. Colour extraction is plain maths: k-means clustering in OKLab, a colour space built around how people perceive difference. The same photo always gives the same palette. Every colour lands in one of 151 entries: 7 neutrals plus 16 hue families × 3 lightness × 3 chroma bands.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted the collection to be completable, so a test sweeps the whole sRGB cube and checks that all 151 entries can actually be reached. Rarity follows one idea, "how often does nature show this colour": greens and greys are everywhere, vivid blues and violets are not. That gives 42 Common, 42 Uncommon, 37 Rare, 21 Legendary and 9 Mythical.&lt;/p&gt;

&lt;p&gt;The models do only what models are good at: judging a scene, and naming things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemma 3 270M, the namer that fits in your pocket
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The star of FarbeDex is &lt;strong&gt;Gemma 3 270M&lt;/strong&gt;, running on the phone through llama.cpp as a 253 MB file. When you register colours, Gemma names each new one, gives the whole palette a one-word mood (COOL, EARTHY, SERENE…), and writes a one-line tip on using the palette in an interface.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A 270M model has big ideas and small habits, so it never writes free text into the app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured output only.&lt;/strong&gt; Every request uses a JSON schema compiled into a grammar, and the schema pins exactly which colours Gemma must name. It can't skip one or invent one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma writes the poetry, the app writes the facts.&lt;/strong&gt; Early on, Gemma named a pale blue "Dark Charcoal". Now Gemma writes only the evocative part ("Rooftop Morning"), and the app adds the true colour word ("…Blue"). Colour words are banned from Gemma's half, so a blue is always called blue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate, retry once, then fall back.&lt;/strong&gt; Every name is checked for length, uniqueness and a blocklist. A failed name gets one retry with the reason attached; after that a plain name like "Vivid Pale Blue" takes over, so you're never left waiting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My first Legendary indigo came back as &lt;strong&gt;Ocean Waves&lt;/strong&gt;. Here it is in the Tailwind export, next to the plain fallback names of colours Gemma hadn't named yet:&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%2Fxffyrd994elkph8rql0o.jpeg" 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%2Fxffyrd994elkph8rql0o.jpeg" alt="Tailwind Export" width="720" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More names Gemma gave my catches: &lt;em&gt;[2–4 more names from your catches]&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  MobileCLIP, the referee that keeps it honest
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The second open model is &lt;strong&gt;MobileCLIP-S0&lt;/strong&gt; on ONNX Runtime. It answers two questions: is this a real outdoor scene, and what's in it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The phone never runs a text encoder. I embedded every label on my laptop ("a computer monitor", "potted plants on a balcony", "the blue sky"…) and ship them as a small file, so on the phone one image embedding and a few dot products do the job. It rejects only confident screens and home interiors, so balconies, rooftops and pot plants all count. And the subject it names must match the photo's real colours.&lt;/p&gt;

&lt;p&gt;Yes, I tried to cheat by photographing my own laptop. It didn't work:&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%2F0ubc2clmxbiefqsh1ew1.jpeg" 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%2F0ubc2clmxbiefqsh1ew1.jpeg" alt="Field Reject" width="720" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick two, and sometimes get lucky
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Catching shouldn't be mindless, so you choose two favourites per catch, and only those join your collection. Rare, Legendary and Mythical picks get a special reveal, and each has a 1-in-400 chance to be &lt;strong&gt;shiny&lt;/strong&gt;: gold rim, sparkles and its own chiptune jingle, all original and generated in code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Two AI teammates, one source of truth
&lt;/h2&gt;

&lt;p&gt;I built FarbeDex in about two and a half days with two AI tools, each with its own job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot is the editor.&lt;/strong&gt; While Claude builds, Copilot keeps the documentation updated in parallel, so the docs never fall behind the code. When each Claude task finishes, Copilot curates the commit message, turning a long session into one clean line of history.&lt;/p&gt;

&lt;p&gt;Specs go in; tested, documented commits come out. The repo ships with 279 passing tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  We Touched Grass
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm a graphic designer and a front-end developer, which mostly means I'm a person at a desk. My whole working day happens on a laptop. When the work stops, the scrolling starts: my "free time" was Instagram Reels until my thumb got tired.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I didn't notice what it was doing to me until it had already done it. I went out less, talked to people less, and slowly got bad at simple things: starting a conversation, being somewhere without a screen, just being outside. Climbing back out took real, slow work, and I did most of it alone. Nobody hands you a plan for that.&lt;/p&gt;

&lt;p&gt;So I know exactly where the problem sits. People like me know the outdoors exists. What we lack is a reason to step out that doesn't come with pressure. Every reason the internet offers is another task: 10,000 steps, a streak, a specific bird. When you're already drained, one more task is exactly what keeps you at your desk.&lt;/p&gt;

&lt;p&gt;FarbeDex is built for that version of me. For a designer, colour &lt;em&gt;is&lt;/em&gt; the reason. There's no target and no wrong answer. You walk to the corner, notice the light on a wall, and tap once. That moment becomes something real for your work, and a little colour pal you actually want to show people. Everything runs on the phone, so you can leave the Wi-Fi, and the feed, behind.&lt;/p&gt;

&lt;p&gt;Then comes the moment later, back at the desk. You remember that evening and think: &lt;em&gt;those colours were so good, I wish I had them in my design.&lt;/em&gt; Now you do, in five formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Palette image&lt;/strong&gt;: a swatch card with a one-word mood, ready for a moodboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HEX codes&lt;/strong&gt;: copy any colour with one tap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS&lt;/strong&gt;: custom properties to paste into a stylesheet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind&lt;/strong&gt;: a &lt;code&gt;colors&lt;/code&gt; block for your config.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON&lt;/strong&gt;: design tokens for Figma plugins and design systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A built-in contrast table even tells you which pairs pass WCAG for text, so the sky you saw can become a readable website.&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%2F99ei17zgj69nevmbll70.jpeg" 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%2F99ei17zgj69nevmbll70.jpeg" alt="Color Pallet share" width="720" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what happened when I used it myself:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Your first walks with FarbeDex: balcony, roof, the street near home. How long you stayed out, how many colours you caught, your rarest find, a shiny if you got one, and what it felt like to look up instead of down. 2–3 photos.]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Your FarbeDex counter before and after, e.g. "0 / 151 → 2 / 151".]&lt;/em&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It works where colour lives.&lt;/strong&gt; Parks, rooftops and planes often have no signal. Both models (276 MB together) run fully on the phone, in airplane mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your photos stay yours.&lt;/strong&gt; Nothing is uploaded, there's no account, and the only permission FarbeDex asks for is the camera.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing per catch.&lt;/strong&gt; No API bill, no rate limit, and it will work exactly the same next year.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can see inside it.&lt;/strong&gt; Every prompt, label and threshold is in the repo. Don't like the names? Change the prompt. Want a different referee? It's one config line.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkt4yca13izllwpgqzs9g.jpeg" 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%2Fkt4yca13izllwpgqzs9g.jpeg" alt="In your phone" width="720" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A big closed vision model might label scenes a little better. But it would need every outdoor photo uploaded, a signal in the middle of a park, and money for each catch. For noticing colours on a walk, two tiny open models are exactly enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Didn't Work
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The quantized referee saw screens everywhere.&lt;/strong&gt; The int8 version of MobileCLIP called a beach, a lake and a savanna "a screen". Switching to the fp16 file (23 MB) fixed it completely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My own balcony counted as indoors.&lt;/strong&gt; On the phone, every shot from my balcony and roof was rejected as "inside". I flipped the logic: accept by default, and reject only when the model is sure it sees a screen or a home interior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A sky called "fern".&lt;/strong&gt; One prompt per subject wasn't enough. Several prompts per subject, plus a rule that the label must match the photo's real colours, fixed it. No green, no fern.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 3 270M runs fully offline on an Android phone, naming colours, giving palettes a mood and writing design tips through validated, schema-constrained JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  Team
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Subarna Maity - Me&lt;/em&gt;&lt;br&gt;
*Sourish Panda - &lt;a class="mentioned-user" href="https://dev.to/t-rexbytes"&gt;@t-rexbytes&lt;/a&gt; *&lt;/p&gt;




&lt;p&gt;I set out to build a colour picker that makes you go outside. What I ended up with is an app that, most days, just asks you to look up. 🌿&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>android</category>
    </item>
    <item>
      <title>The groove is the 40 milliseconds a grid throws away, so we built a beat studio that gives them back, in a browser tab</title>
      <dc:creator>Subarna Maity</dc:creator>
      <pubDate>Sun, 04 Oct 2026 20:41:51 +0000</pubDate>
      <link>https://dev.to/dronzer2code/the-groove-is-the-40-milliseconds-a-grid-throws-away-so-we-built-a-beat-studio-that-gives-them-2adg</link>
      <guid>https://dev.to/dronzer2code/the-groove-is-the-40-milliseconds-a-grid-throws-away-so-we-built-a-beat-studio-that-gives-them-2adg</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At 90 BPM a sixteenth note lasts &lt;strong&gt;166.7 ms&lt;/strong&gt;. Swing at 50% pushes every second one of them late by &lt;strong&gt;41.7 ms&lt;/strong&gt;. That is the entire difference between a hi-hat that marches and one that bounces, and it is shorter than one frame of most phone videos.&lt;/p&gt;

&lt;p&gt;A plain step grid snaps every hit onto those 166.7 ms boundaries and throws the 40 ms away. We built one that keeps them, and then asked a harder question.&lt;/p&gt;

&lt;p&gt;The single most common mistake in a multi-sponsor project is using each sponsor as a logo. A drum machine that stores a JSON blob somewhere is not a database project. A text box that calls an API once is not a sound-design project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So the first question was: what can each piece do that the others physically cannot?&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;The one thing only it can do here&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The browser&lt;/td&gt;
&lt;td&gt;Play audio with sample-accurate timing and run open-source models on the user's own device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tiger Data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keep workspaces, audio, 384-d embeddings and a full-text index in &lt;strong&gt;one&lt;/strong&gt; Postgres, and answer "find me a warm, dusty kick" with a single SQL statement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ElevenLabs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Produce a sound that exists in no sample pack, from a sentence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Entire&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keep the agent's reasoning next to the commit it produced, so the build itself is reviewable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;My friend &lt;strong&gt;Anuv&lt;/strong&gt; makes beats, and his ideas don't wait for him to be at his setup.Krisp is what we built him.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Krisp&lt;/strong&gt; is a minimal, hardware-feel beat studio in a browser tab. You tap a beat on pads, give it feel with velocity and swing, let an open-source model humanize it or offer four variations, describe a sound and get it, find any sound again by its vibe, arrange beats into a song, mix it through a real master chain, and export a WAV, or MIDI for your DAW.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; pads, keys --&amp;gt; zustand store --&amp;gt; scheduler -------&amp;gt; Tone.js engine --&amp;gt; speakers
 (tap, paint,   one Workspace,    pure timing math,  channels, reverb,
  velocity,     undo history      100 ms look-ahead  comp + limiter    same engine
  live REC)          |                                   |        --&amp;gt; Tone.Offline --&amp;gt; WAV
                     |--&amp;gt; Magenta.js (GrooVAE, MusicVAE, DrumRNN)   in the browser
                     |--&amp;gt; all-MiniLM-L6-v2 (384-d embeddings)       in the browser
                     |
                     '--&amp;gt; /api (Vercel) --&amp;gt; Tiger Data: Postgres + pgvector + full-text
                                       '--&amp;gt; ElevenLabs Sound Effects (server-side key)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  One thing up front, because it changes how you read every number below
&lt;/h3&gt;

&lt;p&gt;Every number in this post was &lt;strong&gt;measured&lt;/strong&gt;, and here is where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audio and AI timings: &lt;strong&gt;headless Chrome on a Windows laptop with no GPU&lt;/strong&gt;, against a local dev server. A laptop with a GPU will be faster. I haven't measured that, so I don't claim it.&lt;/li&gt;
&lt;li&gt;Database latency: &lt;strong&gt;India to a Tiger Data service in US-East&lt;/strong&gt;. The deployed functions run in Vercel's &lt;code&gt;iad1&lt;/code&gt;, next to the database.&lt;/li&gt;
&lt;li&gt;Search measurements used &lt;strong&gt;synthetic test sounds&lt;/strong&gt; that the test script generated, uploaded and deleted again.&lt;/li&gt;
&lt;li&gt;Where something broke, the error is quoted &lt;strong&gt;verbatim&lt;/strong&gt; in What Didn't Work.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Demo Video
&lt;/h3&gt;

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

&lt;p&gt;Six stops, in the order you meet them.&lt;/p&gt;
&lt;h4&gt;
  
  
  1 · The dashboard
&lt;/h4&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%2F4wpdrf4js613ilxmvqfi.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%2F4wpdrf4js613ilxmvqfi.png" alt="demo1" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every idea is a card. Its colour strip is the first six beats inside it, so a sketch is recognisable before you open it. Under the cards, a hand-lettered guide teaches the whole app with real controls you can touch.&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%2Flqqwo2q2vi9muqu8frtn.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%2Flqqwo2q2vi9muqu8frtn.png" alt="demo2" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For Anuv:&lt;/strong&gt; his ideas stop living in voice memos and stray files. They are all one click from Home, and he never needs a manual.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  2 · Creating a workspace
&lt;/h4&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%2Fv24w2vq21z8r594ny7w8.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%2Fv24w2vq21z8r594ny7w8.png" alt="demo3" width="591" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start empty, or from one of eight original patterns, each with its own tempo and swing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For Anuv:&lt;/strong&gt; a groove is playing seconds after he opens the app, as a starting point he reshapes by hand, not a blank grid.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  3 · The full workspace
&lt;/h4&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%2Fup3hvq1tieiwkzons7b6.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%2Fup3hvq1tieiwkzons7b6.png" alt="demo4" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One screen: the transport on top, the Rack on the left, the Beat Editor in the centre, the Song underneath, Sounds and AI on the right. Saving is automatic. The LED goes amber, then green.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For Anuv:&lt;/strong&gt; nothing hides behind menus or modes. Beat, song and sounds are all in view while the idea is still warm.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  4 · Left side: the Rack and the Beat Editor
&lt;/h4&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%2Fy6da84bp551ru8uuuwa5.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%2Fy6da84bp551ru8uuuwa5.png" alt="demo5" width="799" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each Rack row is one sound, with tune, mute, solo and a preview pad, and it &lt;em&gt;is&lt;/em&gt; the row header of the grid beside it: same height, same order. Click to place a hit, drag to paint a row, Shift-click for a ghost note or an accent, or hit REC and play it in from the keyboard.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For Anuv:&lt;/strong&gt; he programs the way he already thinks, and the grid keeps the velocity and swing that make it his.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  5 · Right side: Sounds and AI
&lt;/h4&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%2F3354t9b2anvorbujiold.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%2F3354t9b2anvorbujiold.png" alt="demo6" width="399" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Four tabs: Presets, Create (ElevenLabs), Upload and Library. The Library sorts sounds into playlists and searches by name or by vibe. The AI tab next to it holds Humanize, Variations, Continue and Morph (Stage 4 below).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For Anuv:&lt;/strong&gt; an evening of digging through sample packs becomes one sentence and one search box.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  6 · The mixer
&lt;/h4&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%2Fu6vj88a7cdevbgiy9n0d.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%2Fu6vj88a7cdevbgiy9n0d.png" alt="demo7" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;M&lt;/strong&gt;. Every sound gets a strip with EQ, reverb send, pan and fader. Then a reverb return, and a master with a compressor, gain-reduction LEDs and a limiter.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For Anuv:&lt;/strong&gt; a sketch leaves Krisp already mixed, so the WAV he sends sounds finished, not like a demo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://krisp.vercel.app" rel="noopener noreferrer"&gt;https://krisp.vercel.app&lt;/a&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Password : subaru2007&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Saving and the sound library sit behind a studio passcode, because ElevenLabs credits cost money. The video shows them. Everything in the next list works without it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Verify this in 60 seconds, no keys, no clone
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://krisp.vercel.app" rel="noopener noreferrer"&gt;https://krisp.vercel.app&lt;/a&gt; → &lt;strong&gt;New workspace&lt;/strong&gt; → &lt;strong&gt;Lo-fi&lt;/strong&gt; → &lt;strong&gt;Create&lt;/strong&gt;. If it asks for a passcode, click &lt;strong&gt;Not now&lt;/strong&gt;. You can still make music; it just won't save.&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Space&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Shift-click pads in the snare row. They cycle ghost → soft → normal → accent. Turn &lt;strong&gt;Swing&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI&lt;/strong&gt; tab → &lt;strong&gt;Humanize&lt;/strong&gt;. The first run downloads the model. Then tiny dots appear inside the pads: that's microtiming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export&lt;/strong&gt; → &lt;strong&gt;WAV&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  The check I would make if I were judging this
&lt;/h3&gt;

&lt;p&gt;Drop that WAV into Audacity and zoom into the hi-hats. At 90 BPM with 50% swing, every second hat must land about &lt;strong&gt;41 ms&lt;/strong&gt; late, and the even ones must not move.&lt;/p&gt;

&lt;p&gt;When I ran exactly this against Krisp's own export, the odd hats landed &lt;strong&gt;0.24 of a step&lt;/strong&gt; late (40 ms against the formula's 41.7 ms) and the even hats moved by less than 0.02 of a step. That is how you know the WAV is the same engine you were listening to, and not a re-implementation of it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Dronzer2code" rel="noopener noreferrer"&gt;
        Dronzer2code
      &lt;/a&gt; / &lt;a href="https://github.com/Dronzer2code/Krisp" rel="noopener noreferrer"&gt;
        Krisp
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Krisp — a beat workspace that runs in the browser&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Krisp is a calm, hardware-feel beat workspace built for one producer friend. Make beats by hand on a step
sequencer, arrange them into a song, mix them with channel strips and a master chain, and export WAV and MIDI for
your DAW. Open-source AI (Magenta.js) runs &lt;strong&gt;in your browser&lt;/strong&gt; to humanize and vary your own beats. ElevenLabs
creates the exact one-shot or loop you describe, and a Tiger Data hybrid search finds any sound again by its
name or by its sound ("warm dusty kick").&lt;/p&gt;
&lt;p&gt;Built for the DEV &lt;strong&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/strong&gt; (October 2026).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;em&gt;URL added after deploy&lt;/em&gt; (the sound library and saving are behind a studio passcode)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs for agents and contributors:&lt;/strong&gt; &lt;a href="https://github.com/Dronzer2code/Krisp/CLAUDE.md" rel="noopener noreferrer"&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/a&gt; → &lt;a href="https://github.com/Dronzer2code/Krisp/docs/" rel="noopener noreferrer"&gt;&lt;code&gt;docs/&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Features&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Workspaces&lt;/td&gt;
&lt;td&gt;Create (empty or from 8 original preset beats), open, rename, duplicate, delete;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Dronzer2code/Krisp" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;Krisp was built with Claude Code and captured with Entire (see Stage 7):&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://entire.io/et/krisp/krisp" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fentire.io%2Fimages%2Fog%2Fhome.png%3Fv%3D10" height="630" class="m-0" width="1200"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://entire.io/et/krisp/krisp" rel="noopener noreferrer" class="c-link"&gt;
            Overview · krisp/krisp · Entire
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Entire captures your AI agent sessions and stores them alongside every commit.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fentire.io%2Ffavicon.svg" width="32" height="32"&gt;
          entire.io
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Created inside the challenge window (first commit 2026-10-03). Open-source work Krisp stands on is credited in the README: &lt;strong&gt;Magenta.js&lt;/strong&gt;, &lt;strong&gt;Tone.js&lt;/strong&gt;, &lt;strong&gt;transformers.js&lt;/strong&gt; with &lt;strong&gt;all-MiniLM-L6-v2&lt;/strong&gt;, &lt;strong&gt;@tonejs/midi&lt;/strong&gt;, &lt;strong&gt;pgvector&lt;/strong&gt;, and &lt;strong&gt;Petaluma Script&lt;/strong&gt; for the hand-lettered guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run it yourself
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Dronzer2code/Krisp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;Krisp
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env    &lt;span class="c"&gt;# TIGER_DATABASE_URL, ELEVENLABS_API_KEY, APP_PASSCODE, DAILY_SOUND_LIMIT&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="c"&gt;# paste db/schema.sql into the Tiger Data SQL editor once&lt;/span&gt;
npx vercel dev          &lt;span class="c"&gt;# app + /api on http://localhost:3000&lt;/span&gt;
npm run &lt;span class="nb"&gt;test&lt;/span&gt;            &lt;span class="c"&gt;# 146 unit tests&lt;/span&gt;
npm run build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; bash scripts/check-secrets.sh   &lt;span class="c"&gt;# no key may appear in dist/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  The architecture, actually explained
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;th&gt;Where it runs&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;zustand store&lt;/td&gt;
&lt;td&gt;browser&lt;/td&gt;
&lt;td&gt;One Workspace object, undo history; a whole knob drag is one undo step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Scheduler&lt;/td&gt;
&lt;td&gt;browser&lt;/td&gt;
&lt;td&gt;Pure timing math on Tone.js's Transport, 100 ms look-ahead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Audio engine&lt;/td&gt;
&lt;td&gt;browser&lt;/td&gt;
&lt;td&gt;Channels → reverb → master EQ → compressor → limiter → ceiling clipper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Magenta.js&lt;/td&gt;
&lt;td&gt;browser&lt;/td&gt;
&lt;td&gt;Humanize, Variations, Continue, Morph on the user's own beat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;ElevenLabs&lt;/td&gt;
&lt;td&gt;server&lt;/td&gt;
&lt;td&gt;Text → one-shot or seamless loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Tiger Data&lt;/td&gt;
&lt;td&gt;server&lt;/td&gt;
&lt;td&gt;Workspaces (JSONB), sounds + embeddings (pgvector), full-text, playlists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Entire&lt;/td&gt;
&lt;td&gt;git&lt;/td&gt;
&lt;td&gt;Agent sessions as checkpoints, linked to the commits they produced&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rule behind every row: &lt;strong&gt;anything musical happens in the browser and never waits on the network.&lt;/strong&gt; The network is for saving, the library, and making new sounds. Nothing else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 2: the scheduler is three pure functions
&lt;/h3&gt;

&lt;p&gt;A drum machine in a browser is a timing problem before it's anything else. All of Krisp's timing is three functions you can unit-test without a sound card:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stepSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;bpm&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;swingDelay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stepIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;swing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stepSec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;stepIndex&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;swing&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;stepSec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;hitTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tickTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stepIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;swing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stepSec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tickTime&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;swingDelay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stepIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;swing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stepSec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;stepSec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tickTime&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;stepSec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// a humanized hit may lead, never into the previous step&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Transport never loops. The loop region is applied by one more pure function, &lt;code&gt;wrapTick&lt;/code&gt;, so drum hits and audio clips are triggered by the &lt;strong&gt;same&lt;/strong&gt; per-tick code and can't drift apart.&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%2F92lmvhycadn795g44ogl.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%2F92lmvhycadn795g44ogl.png" alt="demo8" width="800" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 3: the export is not a second renderer
&lt;/h3&gt;

&lt;p&gt;The obvious way to make a WAV is to write an offline renderer. That gives you two engines, and they will eventually disagree. &lt;code&gt;createEngine(context)&lt;/code&gt; accepts &lt;em&gt;any&lt;/em&gt; audio context, so the export opens a &lt;code&gt;Tone.Offline&lt;/code&gt; context and runs the same graph and the same scheduler through it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Tone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createEngine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// identical graph: channels, reverb, master chain&lt;/span&gt;
  &lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&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;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                    &lt;span class="c1"&gt;// reverb impulse response&lt;/span&gt;
  &lt;span class="nf"&gt;bindScheduler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;getWorkspace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trigger&lt;/span&gt; &lt;span class="cm"&gt;/* … */&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;44100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 16-bar song at 90 BPM is 42.7 s of music. With a 2 s reverb tail, it rendered in &lt;strong&gt;16.1 s&lt;/strong&gt; on that GPU-less laptop, about 2.7× faster than real time. It also followed the arrangement exactly: audio in bars 1–4, then RMS &lt;strong&gt;0.000000&lt;/strong&gt; after.&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%2Fzpxq4ktfssk1yhhy6xfw.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%2Fzpxq4ktfssk1yhhy6xfw.png" alt="demo9" width="521" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 4: the AI never writes the beat for you
&lt;/h3&gt;

&lt;p&gt;Most AI music tools generate the song. Krisp's AI only touches the beat you made, and every result is one undo away.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Open checkpoint&lt;/th&gt;
&lt;th&gt;What it is allowed to change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Humanize&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;groovae_2bar_humanize&lt;/code&gt;, trained on the Groove MIDI Dataset of real drummers&lt;/td&gt;
&lt;td&gt;Velocity and timing of steps that are &lt;strong&gt;already on&lt;/strong&gt;. It can't add or remove a note&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Variations&lt;/td&gt;
&lt;td&gt;&lt;code&gt;drums_2bar_lokl_small&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Four candidates near your beat; &lt;em&gt;wildness&lt;/em&gt; sets similarity and temperature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Continue&lt;/td&gt;
&lt;td&gt;&lt;code&gt;drum_kit_rnn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Writes bar 2 from bar 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Morph&lt;/td&gt;
&lt;td&gt;&lt;code&gt;drums_2bar_lokl_small&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nine steps from one beat to another&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before Humanize, every hit sits dead on the grid:&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%2Fexi0aqj2m2uxjss4ap04.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%2Fexi0aqj2m2uxjss4ap04.png" alt="demo10" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After, the same hits, nothing added or removed. Velocities changed (brightness), and each dot is a microtiming offset the model put there:&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%2Fw0xp1t3j0sjl6z3ybyhd.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%2Fw0xp1t3j0sjl6z3ybyhd.png" alt="demo11" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Variations are previewed by &lt;em&gt;holding&lt;/em&gt; a candidate. It plays in place of your beat for as long as you hold it, without touching the beat or the undo history.&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%2F66ic386o6adki7thnx4j.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%2F66ic386o6adki7thnx4j.png" alt="demo12" width="616" height="718"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two facts I only learned by running the models are now built into the converter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MusicVAE and DrumRNN return quantized notes with &lt;strong&gt;velocity unset&lt;/strong&gt;. It reads as 0, so Krisp defaults it to 100.&lt;/li&gt;
&lt;li&gt;GrooVAE returns &lt;strong&gt;unquantized&lt;/strong&gt; notes: a start time, not a step. Krisp recovers the step as &lt;code&gt;round(startTime / stepSeconds)&lt;/code&gt; and keeps the remainder as an offset clamped to ±½ step. That remainder &lt;em&gt;is&lt;/em&gt; the groove.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And one I had to design around: Snare and Clap share drum pitch 38. A model's snare notes would wipe out the clap row, so model notes go to the &lt;strong&gt;first&lt;/strong&gt; slot with a pitch and the rest keep their own steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 5: ElevenLabs, the sound that isn't in any pack
&lt;/h3&gt;

&lt;p&gt;Digging through sample packs is search over things that already exist. ElevenLabs makes the thing that doesn't. That's a different job, and it changes the workflow: the producer says what they hear, and the sound turns up on a pad.&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%2Fk0c4mduxw8598lqvjwot.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%2Fk0c4mduxw8598lqvjwot.png" alt="demo13" width="399" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Krisp shapes the request and &lt;strong&gt;shows you exactly what it sends&lt;/strong&gt;, so nothing is hidden behind a magic box:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;ONE_SHOT  "&amp;lt;your words&amp;gt;, single drum one-shot, isolated, dry, no music"
LOOP      "&amp;lt;your words&amp;gt;, seamless drum loop, &amp;lt;bpm&amp;gt; bpm"        + loop: true

POST https://api.elevenlabs.io/v1/sound-generation?output_format=mp3_44100_128
     xi-api-key: (server only)   { text, duration_seconds, prompt_influence: 0.5, loop }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Everything ElevenLabs is actually doing here
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Why it's load-bearing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sound Effects API&lt;/td&gt;
&lt;td&gt;&lt;code&gt;api/sound-generate.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The only way to get a sound that exists in no pack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;loop: true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;loop requests&lt;/td&gt;
&lt;td&gt;Seamless loops that drop straight onto an AUDIO lane&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;duration_seconds&lt;/code&gt; 0.5–2 s&lt;/td&gt;
&lt;td&gt;one-shots&lt;/td&gt;
&lt;td&gt;Drum hits, not soundscapes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server-side key&lt;/td&gt;
&lt;td&gt;Vercel function&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;scripts/check-secrets.sh&lt;/code&gt; fails the build if a key leaks into &lt;code&gt;dist/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily limit&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sound_usage&lt;/code&gt; table&lt;/td&gt;
&lt;td&gt;40 credits per second of sound; the counter ("38 left today") keeps the demo alive through judging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The two generated sounds in this library came back at &lt;strong&gt;1.3 s&lt;/strong&gt; each. In the browser they are trimmed of silence, faded, peak-normalised to −1 dBFS, embedded, and filed next to uploads:&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%2F02pu04o33898wgafiqvy.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%2F02pu04o33898wgafiqvy.png" alt="demo14" width="402" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 6: Tiger Data, one Postgres with three kinds of memory
&lt;/h3&gt;

&lt;p&gt;Producers search two ways. Sometimes it's a word: "808", "clap". Sometimes it's a feeling: "warm", "dusty", "like old vinyl". Keyword search fails the second; vector search fumbles the first. The usual answer is a Postgres &lt;em&gt;and&lt;/em&gt; a vector database &lt;em&gt;and&lt;/em&gt; glue code between them.&lt;/p&gt;

&lt;p&gt;Tiger Data made that a non-problem. One service holds the workspaces as &lt;strong&gt;JSONB&lt;/strong&gt;, the audio as &lt;strong&gt;bytea&lt;/strong&gt;, the meaning as &lt;strong&gt;pgvector&lt;/strong&gt; and the spelling as &lt;strong&gt;tsvector&lt;/strong&gt;. The fusion is a single statement, reciprocal rank fusion with k = 60 (columns abridged):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;            &lt;span class="c1"&gt;-- meaning: cosine distance on all-MiniLM-L6-v2 embeddings made in the browser&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row_number&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sounds&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;              &lt;span class="c1"&gt;-- spelling: Postgres full-text&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row_number&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;ts_rank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tsv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;websearch_to_tsquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tsv&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;ts_rank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tsv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;vector_hit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;keyword_hit&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sounds&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every result carries a &lt;strong&gt;&lt;code&gt;vector&lt;/code&gt;&lt;/strong&gt; badge, a &lt;strong&gt;&lt;code&gt;keyword&lt;/code&gt;&lt;/strong&gt; badge, or both, so you can see &lt;em&gt;why&lt;/em&gt; a sound came up. In testing, &lt;em&gt;"dusty kick"&lt;/em&gt; found the uploaded kick with both badges. &lt;em&gt;"warm lo-fi bass drum"&lt;/em&gt; (words that were never its name) found it by vector, plus a keyword hit on its &lt;code&gt;warm&lt;/code&gt; tag.&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%2Fdplft3ava9iauy35ounl.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%2Fdplft3ava9iauy35ounl.png" alt="demo15" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The library is organised the way producers organise folders: &lt;strong&gt;playlists&lt;/strong&gt;, where a sound can live in several at once, sit above everything still unsorted (you saw them in the walkthrough).&lt;/p&gt;

&lt;p&gt;This is the whole schema on Tiger Data, live:&lt;/p&gt;

&lt;h4&gt;
  
  
  Everything Tiger Data is actually doing here
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Why it's load-bearing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;pgvector (&lt;code&gt;vector(384)&lt;/code&gt;, HNSW cosine)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sounds.embedding&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search by vibe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full-text (&lt;code&gt;tsvector&lt;/code&gt;, GIN)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sounds.tsv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search by exact word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-statement RRF&lt;/td&gt;
&lt;td&gt;&lt;code&gt;api/search.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Both searches, one round trip, ranked together&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSONB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;workspaces.data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A whole Workspace (rack, beats, song, mix) in one row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bytea&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sounds.audio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Audio lives with its metadata. No second storage service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Two-table join&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;playlists&lt;/code&gt; + &lt;code&gt;playlist_sounds&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Folders where a sound can be in many&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Stage 7: Entire, the build is reviewable too
&lt;/h3&gt;

&lt;p&gt;A project built with an AI agent has a second artefact besides the code: the reasoning that produced it. Entire keeps it. We ran &lt;code&gt;entire enable --agent claude-code --import-history&lt;/code&gt;. That installed Claude Code hooks, and it also &lt;strong&gt;imported the session that had started before Entire was set up&lt;/strong&gt;, so nothing from the first hour was lost. Since then, every &lt;code&gt;git push origin&lt;/code&gt; uploads the session checkpoints next to the commits they produced. The repository itself lives on Entire (&lt;code&gt;origin&lt;/code&gt;), mirrored to GitHub for Vercel.&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%2Ff2bcoqlw3gzyz4o08503.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%2Ff2bcoqlw3gzyz4o08503.png" alt="demo16" width="799" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Entire's own numbers for this repo: &lt;strong&gt;36 commits&lt;/strong&gt;, &lt;strong&gt;7 checkpoints&lt;/strong&gt;, &lt;strong&gt;15 messages&lt;/strong&gt;, &lt;strong&gt;591.3k tokens&lt;/strong&gt; (996 in, 590.3k out), about &lt;strong&gt;84.5k tokens per checkpoint&lt;/strong&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%2Fhshku1n9jhxhm4bklyu9.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%2Fhshku1n9jhxhm4bklyu9.png" alt="demo17" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is what made the "verbatim errors" table below possible. Every failure, and the reasoning that fixed it, is on record rather than reconstructed from memory.&lt;/p&gt;

&lt;h4&gt;
  
  
  Everything Entire is actually doing here
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Why it's load-bearing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code hooks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sessions captured as they happen, no copy-pasting transcripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--import-history&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;first setup&lt;/td&gt;
&lt;td&gt;Recovered the session from before Entire existed in the repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checkpoints on &lt;code&gt;git push&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;entire://…/et/krisp/krisp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reasoning travels with the code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics&lt;/td&gt;
&lt;td&gt;Entire dashboard&lt;/td&gt;
&lt;td&gt;Measurable build: commits, checkpoints, tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What My Friend Said
&lt;/h2&gt;

&lt;p&gt;[[HANDOVER: what Anuv did on camera — the tune he made, what he exported, what he took into his DAW. Facts only.]]&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[[QUOTE: Anuv's own words, exactly as he said them]]&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Four concrete reasons, each one measured or tested:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The beat never leaves the device.&lt;/strong&gt; I ran the whole AI test suite with &lt;strong&gt;every &lt;code&gt;/api&lt;/code&gt; request blocked&lt;/strong&gt; in the browser. Humanize, Variations, Continue and Morph all passed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run.&lt;/strong&gt; No per-request inference bill for the AI. The only metered call in Krisp is ElevenLabs, and it's explicit and capped per day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open means inspectable.&lt;/strong&gt; I only found out that GrooVAE returns unquantized start times, and that the VAE leaves velocity at 0, &lt;em&gt;because&lt;/em&gt; the checkpoints and the library are open. A closed API would have handed back a groove I couldn't explain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Once loaded, it is always there.&lt;/strong&gt; The cold cost is real: the drums VAE loaded in &lt;strong&gt;14.2 s&lt;/strong&gt; and the first Humanize took &lt;strong&gt;15 s&lt;/strong&gt;, both on a GPU-less machine. Both happen once. [[MEASURE: warm Humanize time on Anuv's laptop]]&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What this unlocks
&lt;/h3&gt;

&lt;p&gt;The same pattern (a pure scheduler, one engine for live and offline, open models in the tab, one Postgres for meaning and spelling) works for any instrument, not just drums. A melodic step sequencer, a sample chopper or a practice metronome with real feel is a new rack, not a new architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Didn't Work
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hazards, with the errors verbatim
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;th&gt;The error&lt;/th&gt;
&lt;th&gt;The fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Postgres refused my search column&lt;/td&gt;
&lt;td&gt;&lt;code&gt;generation expression is not immutable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;array_to_string&lt;/code&gt; isn't immutable, so &lt;code&gt;tsv&lt;/code&gt; became a plain column that each INSERT fills&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A new Tiger service failed TLS for over an hour&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELF_SIGNED_CERT_IN_CHAIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The cert came from Tiger's own CA, &lt;code&gt;ca.timescale.com&lt;/code&gt;. I trusted that CA &lt;strong&gt;with verification on&lt;/strong&gt;, instead of &lt;code&gt;rejectUnauthorized: false&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disabling the limiter crashed the mixer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RangeError: Value must be within [-100, 0], got: 1e-7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tone ramps start from 1e-7 when a param is 0, so dynamics params are now set directly, not ramped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Solo didn't silence the other channels&lt;/td&gt;
&lt;td&gt;no error: the kick still read &lt;strong&gt;−32 dB&lt;/strong&gt; with the snare soloed&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Volume.mute&lt;/code&gt; was undone by the next volume ramp. Mute is now its own gate stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The limiter didn't limit&lt;/td&gt;
&lt;td&gt;no error: on &lt;strong&gt;+6.4 dBFS&lt;/strong&gt;, off &lt;strong&gt;+7.2 dBFS&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;WebAudio's compressor adds makeup gain and lets transients through; even a raw −12 dB / 20:1 left a 0 dB sine at −4.2 dBFS. A soft clipper now holds peaks at exactly &lt;strong&gt;−6.0 dBFS&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript gave up on transformers.js&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Expression produces a union type that is too complex to represent.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A typed wrapper around &lt;code&gt;pipeline()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vercel blocked deploys after a teammate's merge&lt;/td&gt;
&lt;td&gt;status &lt;strong&gt;Blocked&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Hobby only deploys private-repo commits authored by the owner. An owner-authored commit unblocked it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saving felt slow in local dev&lt;/td&gt;
&lt;td&gt;~3 s per request&lt;/td&gt;
&lt;td&gt;2.16 s TLS connect + 0.26 s per query, India → US-East, new connection per request. Production sits next to the DB and keeps its pool&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What's real and what's simplified
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Real&lt;/th&gt;
&lt;th&gt;Simplified, on purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Magenta models running in the browser&lt;/td&gt;
&lt;td&gt;The default kit is synthesised; your own sounds replace it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElevenLabs generation, server-side&lt;/td&gt;
&lt;td&gt;No time-stretching. A warning dot marks loops that don't fit the bar grid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pgvector + full-text hybrid search&lt;/td&gt;
&lt;td&gt;A shared studio passcode instead of accounts; it's built for one person&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The WAV export &lt;em&gt;is&lt;/em&gt; the playback engine&lt;/td&gt;
&lt;td&gt;4/4 only, 16 or 32 steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;146 unit tests + browser end-to-end checks&lt;/td&gt;
&lt;td&gt;A daily generation cap to protect credits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Entire&lt;/strong&gt;: the repository lives on Entire; every agent session is captured, imported from before setup, and linked to the &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Tiger Data&lt;/strong&gt;: one Postgres holding JSONB workspaces, bytea audio, pgvector meaning and full-text spelling, fused in a single SQL statement with visible match reasons.
commits it produced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs&lt;/strong&gt;: generated one-shots and seamless loops inside the workflow, with the exact prompt shown and a daily cap.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Team
&lt;/h2&gt;

&lt;p&gt;TEAM: Subarna Maity (&lt;a class="mentioned-user" href="https://dev.to/dronzer2code"&gt;@dronzer2code&lt;/a&gt;) · Sourish Panda (&lt;a class="mentioned-user" href="https://dev.to/t-rexbytes"&gt;@t-rexbytes&lt;/a&gt;)&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Magenta.js — &lt;a href="https://github.com/magenta/magenta-js" rel="noopener noreferrer"&gt;https://github.com/magenta/magenta-js&lt;/a&gt; · Groove MIDI Dataset — &lt;a href="https://magenta.tensorflow.org/datasets/groove" rel="noopener noreferrer"&gt;https://magenta.tensorflow.org/datasets/groove&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ElevenLabs Sound Effects API — &lt;a href="https://elevenlabs.io/docs/api-reference/text-to-sound-effects/convert" rel="noopener noreferrer"&gt;https://elevenlabs.io/docs/api-reference/text-to-sound-effects/convert&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;pgvector — &lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;https://github.com/pgvector/pgvector&lt;/a&gt; · Tiger Data — &lt;a href="https://www.tigerdata.com" rel="noopener noreferrer"&gt;https://www.tigerdata.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Entire — &lt;a href="https://docs.entire.io" rel="noopener noreferrer"&gt;https://docs.entire.io&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built within the challenge window by Subarna Maity and Sourish Panda with AI pair programming, captured on Entire. All preset beat patterns are original to this project. The sounds used in the search measurements were synthetic test tones, generated, uploaded and deleted by the test script. Anuv's name and words appear with his permission.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The System Never Checked If You Slept. Ours Reads Your Pulse First.</title>
      <dc:creator>Subarna Maity</dc:creator>
      <pubDate>Fri, 25 Sep 2026 20:43:24 +0000</pubDate>
      <link>https://dev.to/dronzer2code/the-system-never-checked-if-you-slept-ours-reads-your-pulse-first-2hai</link>
      <guid>https://dev.to/dronzer2code/the-system-never-checked-if-you-slept-ours-reads-your-pulse-first-2hai</guid>
      <description>&lt;h2&gt;
  
  
  Let's Start With Hackathon Scene
&lt;/h2&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Main video is made by the Organizers of Hacktropica this my edited version on it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Right now, while you read this, your face is changing colour.&lt;/p&gt;

&lt;p&gt;Not by much. Every heartbeat pushes a pulse of blood into the capillaries under your skin. Haemoglobin soaks up green light a little more than the tissue around it, so your cheeks darken and fade, darken and fade, roughly once a second. The change is far too small for anyone looking at you to notice.&lt;/p&gt;

&lt;p&gt;A phone camera notices it anyway.&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%2F5v86ei28kz6ryl2rn0bc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5v86ei28kz6ryl2rn0bc.jpg" alt="Robust blood pressure measurement from facial videos in diverse environments" width="762" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You have never seen it happen. It has happened every second of your life.&lt;/p&gt;

&lt;p&gt;Keep that in mind, because this story runs on three clocks, and they do not run at the same speed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLOCK 1  THE SCAN      10 seconds    a face, a front camera, a pulse
CLOCK 2  THE BUILD     36 hours      four people, one repo, one prize
CLOCK 3  THE READING   5 months      one line in the docs we never read
&lt;/code&gt;&lt;/pre&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%2Fyjvbs2a3ov3jg8kg8zwn.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%2Fyjvbs2a3ov3jg8kg8zwn.png" alt="Three horizontal bars labelled The Scan (10 seconds), The Build (36 hours) and The Reading (5 months later), the last one quoting Presage's docs: " width="800" height="327"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The first clock is the product. The second is how it got made. The third is the reason this post exists, and it does not show up until near the end. When it does, it changes what the first two mean.&lt;/p&gt;
&lt;h2&gt;
  
  
  The System never checked if you slept
&lt;/h2&gt;

&lt;p&gt;In Solo Leveling, a mysterious System hands Sung Jinwoo the same daily quest every single day. 100 push-ups, 100 sit-ups, 100 squats, a 10 km run. Didn't sleep? The System doesn't care. Nearly died yesterday? The System doesn't care.&lt;/p&gt;

&lt;p&gt;Every fitness app is that System. It asks how you feel, lets you tap a smiley face, and then serves the plan it was always going to serve.&lt;/p&gt;

&lt;p&gt;But your face is already telling the camera how you feel. Heart rate and breathing rate are sitting right there in the colour of your skin.&lt;/p&gt;

&lt;p&gt;So the question is not "what workout should I do today?" It is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if the System measured you before it assigned the quest?&lt;/strong&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%2Fetbxb5lbw4k4g85jpy7y.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%2Fetbxb5lbw4k4g85jpy7y.png" alt="Why Its So Linked With Solo Leveling Anime" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is SoloFit ("Arise. Train. Conquer."), a React Native app that reads heart rate from the phone camera, turns it into a readiness score, and only then asks an LLM to write your week. Our team, Kolkatar Rosogollas, built it in 36 hours at HackTropica 2K26, and it won &lt;strong&gt;MLH: Best Use of Presage SDK&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is the whole pipeline, with the real numbers from the 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%2Fw903kzb7ptbhzf6rjyv6.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%2Fw903kzb7ptbhzf6rjyv6.png" alt="Eight-step pipeline: 10 s video, S3 multipart upload, Presage rPPG, HR and RR, readiness 0-100, 3 bands, Gemini 2.5 Flash, 7 hard rules" width="799" height="510"&gt;&lt;/a&gt;&lt;br&gt;
&lt;code&gt;1 scan -&amp;gt; 2 vitals -&amp;gt; 1 score -&amp;gt; 3 bands -&amp;gt; 7 days -&amp;gt; 7 rules&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  One thing up front, because it changes how you read everything below
&lt;/h2&gt;

&lt;p&gt;SoloFit has two ways of getting your vitals, and you need to know which is which before any number in this post means anything.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Measured.&lt;/strong&gt; The video goes to Presage, comes back as heart-rate and breathing-rate samples, and the result is tagged &lt;code&gt;source: "presage_api"&lt;/code&gt;. The scan screen shows a green badge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estimated.&lt;/strong&gt; If anything fails (no API key, no video file, upload error, a 100-second timeout), the service does not throw. It returns &lt;strong&gt;randomly generated vitals&lt;/strong&gt;: heart rate 65-95, breathing 13-20, tagged &lt;code&gt;mock_no_key&lt;/code&gt;, &lt;code&gt;mock_no_video&lt;/code&gt; or &lt;code&gt;mock_api_fallback&lt;/code&gt;. The scan screen shows an amber badge that says &lt;strong&gt;"Estimated Vitals."&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fallback was a deliberate hackathon decision. The code comment says so plainly: &lt;code&gt;Fallback is expected in some environments; keep this non-error in UI/logs.&lt;/code&gt; A demo that crashes on bad venue Wi-Fi is not a demo.&lt;/p&gt;

&lt;p&gt;What that fallback does &lt;em&gt;downstream&lt;/em&gt; is a different story, and the third clock is where I tell it. For now, remember the amber badge.&lt;/p&gt;

&lt;p&gt;And one line that applies to the whole post: Presage's own documentation says its metrics are &lt;strong&gt;for general wellness and informational purposes only, have not been cleared by the FDA, and may not be used for medical diagnosis or treatment.&lt;/strong&gt; SoloFit is a fitness app. It is not a medical device, and nothing below is medical advice.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLOCK 1 · THE SCAN · 10 SECONDS&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;[Daily Quest]&lt;/strong&gt; Look at the camera for ten seconds. Reward: a plan that knows how you slept.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most fitness apps give you a plan built from your goals and your equipment, and the plan is the same on the morning after a full night's sleep as it is the morning after an all-nighter. The README puts it in one line I still like: &lt;em&gt;"Most fitness apps ask the user how they feel. Solofit measures it."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The app runs on three beats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Measure, don't assume.&lt;/strong&gt; A 3-second countdown, then a 10-second front-camera recording. Presage reads your heart rate and breathing rate from that video.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reason, don't template.&lt;/strong&gt; Those vitals, plus your profile (age, goals, equipment, injuries, diet, allergies, budget, cuisine), become a readiness score and a prompt. Gemini 2.5 Flash writes a 7-day workout and diet plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adapt, don't repeat.&lt;/strong&gt; Scan again tomorrow and the plan is rebuilt from tomorrow's body, not yesterday's.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4za27kyd5mefcyx63tl7.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%2F4za27kyd5mefcyx63tl7.png" alt="Three real SoloFit screens: the vitals scan viewfinder, the dashboard with readiness, and the day 1 workout plan" width="800" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every screenshot in this post is the real app, running on web through Playwright with no API keys. That matters later, so remember it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The rest of the app keeps the Solo Leveling &lt;em&gt;mechanics&lt;/em&gt;: quests with XP, a global leaderboard, a community feed, a posture screen that uses MediaPipe, and exercise GIFs so nobody has to guess what a "Bulgarian split squat" is. It doesn't keep the Solo Leveling &lt;em&gt;look&lt;/em&gt;. It had it for part of the weekend, and why it lost it is the best story from the event. That's in Hackathon Experience.&lt;/p&gt;

&lt;p&gt;The stack: React Native on Expo SDK 54, Firebase Auth and Firestore for profiles, the Presage Physiology REST API for vitals, Gemini 2.5 Flash for plans, Groq for fast meal swaps, MongoDB Atlas behind an Express server for the leaderboard and community, and Zustand for state.&lt;/p&gt;
&lt;h3&gt;
  
  
  Three engineering decisions worth stealing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Never trust an LLM's JSON. Validate it against hard rules, repair it, then fall back to code.&lt;/strong&gt;&lt;br&gt;
The LLM does not get the last word on your workout. A validator does (more on the seven rules below). If the plan breaks a rule, the exact violation goes back to the model in a repair prompt. If the repair still breaks a rule, a deterministic plan builder takes over. The user always gets a valid week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Route each call to the model that fits its latency budget.&lt;/strong&gt;&lt;br&gt;
Generating a whole week can take a while, and nobody minds. Swapping one meal while you stand in the kitchen has to feel instant. So meal swaps go to Groq's &lt;code&gt;llama-3.1-8b-instant&lt;/code&gt; in JSON mode at temperature 0.2, and only fall back to Gemini if that fails. Plan generation goes the other way: Gemini first (with a backup key), then Groq's &lt;code&gt;openai/gpt-oss-120b&lt;/code&gt; if Gemini is down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Tag every data point with where it came from.&lt;/strong&gt;&lt;br&gt;
Every vitals object carries a &lt;code&gt;source&lt;/code&gt; string. That one field is what lets the UI tell measured data apart from estimated data. It is also what made the problem in the third clock easy to find.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ipsamurai" rel="noopener noreferrer"&gt;
        ipsamurai
      &lt;/a&gt; / &lt;a href="https://github.com/ipsamurai/solofit" rel="noopener noreferrer"&gt;
        solofit
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Solofit — Real-Time Adaptive AI Fitness Coach&lt;/h1&gt;
&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;A mobile fitness application that uses live biometric data to generate and continuously adapt personalised workout and diet plans — in real time, for every individual.&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#what-is-solofit" rel="noopener noreferrer"&gt;What is Solofit?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#the-problem-we-solve" rel="noopener noreferrer"&gt;The Problem We Solve&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#core-technology-stack" rel="noopener noreferrer"&gt;Core Technology Stack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#presage-smartspectra--the-heart-of-solofit" rel="noopener noreferrer"&gt;Presage SmartSpectra — The Heart of Solofit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#gemini-ai--the-brain-of-solofit" rel="noopener noreferrer"&gt;Gemini AI — The Brain of Solofit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#mongodb--the-social-layer" rel="noopener noreferrer"&gt;MongoDB — The Social Layer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#full-architecture-overview" rel="noopener noreferrer"&gt;Full Architecture Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#key-features" rel="noopener noreferrer"&gt;Key Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#challenges--how-we-solved-them" rel="noopener noreferrer"&gt;Challenges &amp;amp; How We Solved Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#business-model--freemium-proposal" rel="noopener noreferrer"&gt;Business Model — Freemium Proposal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ipsamurai/solofit#getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What is Solofit?&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Solofit is a real-time adaptive AI fitness coach built as a React Native mobile application. It combines clinical-grade biometric analysis, large-language-model reasoning, and a social community layer to deliver hyper-personalised fitness and nutrition plans that update every time a user's physiological state changes.&lt;/p&gt;

&lt;p&gt;Most fitness apps ask the user how they feel. Solofit measures it.&lt;/p&gt;

&lt;p&gt;A 10-second face scan through the phone camera captures…&lt;/p&gt;&lt;/div&gt;


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


&lt;h3&gt;
  
  
  Verify this in 60 seconds, no keys
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The whole Presage pipeline in one function.&lt;/strong&gt; &lt;a href="https://github.com/saviour2/solofit/blob/main/src/services/presageService.js#L223-L278" rel="noopener noreferrer"&gt;&lt;code&gt;presageService.js&lt;/code&gt; L223-L278&lt;/a&gt;. Read the &lt;code&gt;try&lt;/code&gt; block top to bottom: read file, get upload URLs, upload chunks, complete, poll. The &lt;code&gt;catch&lt;/code&gt; at the bottom is the fallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The readiness score.&lt;/strong&gt; &lt;a href="https://github.com/saviour2/solofit/blob/main/src/utils/readinessEngine.js#L23-L65" rel="noopener noreferrer"&gt;&lt;code&gt;readinessEngine.js&lt;/code&gt; L23-L65&lt;/a&gt;. Start at 100, subtract. Then look at lines 62-65, which come up again in the third clock.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The LLM guardrails.&lt;/strong&gt; &lt;a href="https://github.com/saviour2/solofit/blob/main/src/services/geminiService.js#L40-L128" rel="noopener noreferrer"&gt;&lt;code&gt;geminiService.js&lt;/code&gt; L40-L128&lt;/a&gt; for the validator, and &lt;a href="https://github.com/saviour2/solofit/blob/main/src/services/geminiService.js#L1491-L1603" rel="noopener noreferrer"&gt;L1491-L1603&lt;/a&gt; for the generate, validate, repair, fall back chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why the scan is 10 seconds.&lt;/strong&gt; &lt;a href="https://github.com/saviour2/solofit/blob/main/src/screens/ScanScreen.js#L36" rel="noopener noreferrer"&gt;&lt;code&gt;ScanScreen.js&lt;/code&gt; L36&lt;/a&gt;. One line, one comment. Remember it.&lt;/li&gt;
&lt;/ol&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%2F28mmvpb49ajjnufmyk9n.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%2F28mmvpb49ajjnufmyk9n.png" alt="Onboarding welcome screen, diet plan with meal alternatives, and the XP leaderboard" width="800" height="697"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Partner Technologies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Presage (the prize)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What rPPG actually is.&lt;/strong&gt; Remote photoplethysmography. A pulse oximeter clipped to your finger shines light through your skin and watches how much comes back as blood surges with each heartbeat. rPPG does the same thing with ambient light and a camera, from a distance. The colour change is tiny and buried in noise from movement, lighting and compression, so the hard part is not the idea. The hard part is pulling a clean signal out of a shaky phone video, and that is the part Presage does for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we used, exactly.&lt;/strong&gt; The prize is called "Best Use of Presage SDK." What SoloFit ships is the &lt;strong&gt;Presage Physiology REST API&lt;/strong&gt; at &lt;code&gt;https://api.physiology.presagetech.com&lt;/code&gt;, called from React Native with plain &lt;code&gt;fetch&lt;/code&gt;, not the on-device native SDK. The header comment in our service says the flow was &lt;em&gt;"reverse-engineered from official Python client v1.6.0."&lt;/em&gt; In other words, Saikat read Presage's Python client, worked out the HTTP calls it makes, and wrote them again in JavaScript, because there was no drop-in React Native package for what we needed. None of the rest of us could have built this part.&lt;/p&gt;

&lt;p&gt;The flow has five steps:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Call&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /v1/upload-url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send the file size and &lt;code&gt;hr_br: { to_process: true }&lt;/code&gt;. Get back a job &lt;code&gt;id&lt;/code&gt;, an &lt;code&gt;upload_id&lt;/code&gt;, and a list of presigned S3 URLs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PUT&lt;/code&gt; to each S3 URL&lt;/td&gt;
&lt;td&gt;Upload the video in 5 MB chunks. Keep each chunk's &lt;code&gt;ETag&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /v1/complete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send the job id, upload id and the &lt;code&gt;{ ETag, PartNumber }&lt;/code&gt; list. Processing starts in the cloud.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /retrieve-data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Poll every 2.5 s. &lt;code&gt;201&lt;/code&gt; means still processing, &lt;code&gt;200&lt;/code&gt; means results are ready. Give up after 40 tries (100 s).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Parse&lt;/td&gt;
&lt;td&gt;Response looks like &lt;code&gt;{ hr: { "0.5": 72, "1.0": 74, ... }, rr: { ... } }&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here is the heart of it, trimmed from the source:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;upload_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uploadId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;requestUploadUrls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;uploadChunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;completeUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uploadId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;MAX_POLL_ATTEMPTS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;POLL_INTERVAL_MS&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/retrieve-data`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parseResults&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// still processing&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Presage processing timed out&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In plain English: ask Presage where to put the video, put it there in pieces, tell Presage the pieces are all in, then knock on the door every two and a half seconds until the answer is ready or 100 seconds have passed.&lt;/p&gt;

&lt;p&gt;And the parse step, which matters more than it looks:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hrVals&lt;/span&gt; &lt;span class="o"&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;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hr&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;hr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hrVals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;hrVals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...same for rr...&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;heart_rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hr&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;breathing_rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rr&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;stress_level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;inferStress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;presage_api&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;In plain English: take every positive sample in the time series, average them into one number, and label the result as live Presage data. Stress is not measured at all. It is inferred from heart rate alone: above 90 is &lt;code&gt;high&lt;/code&gt;, above 72 is &lt;code&gt;medium&lt;/code&gt;, anything else is &lt;code&gt;low&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hold on to three details from that block: &lt;em&gt;every&lt;/em&gt; sample is averaged, nothing looks at confidence, and there is a &lt;code&gt;?? 15&lt;/code&gt;. All three come back later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Presage made easy.&lt;/strong&gt; The server-side processing. We never touched a colour channel, a bandpass filter or a face tracker. Upload a video, get a heart-rate time series back. For a team with a 36-hour clock, that is the entire reason the feature exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What fought back.&lt;/strong&gt; The upload. Presage's API has a 10 MB file limit, and a phone's front camera fills that quickly. The fix is in &lt;a href="https://github.com/saviour2/solofit/blob/main/src/screens/ScanScreen.js#L36" rel="noopener noreferrer"&gt;&lt;code&gt;ScanScreen.js&lt;/code&gt;&lt;/a&gt;:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RECORD_DURATION_S&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Reduced to keep file under 10MB API limit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plus &lt;code&gt;quality: "480p"&lt;/code&gt;, &lt;code&gt;mute: true&lt;/code&gt; (rPPG does not need audio), and a &lt;code&gt;maxFileSize&lt;/code&gt; cap of 9 MB. The in-code estimate is 2-4 MB for ten seconds, which fits in a single 5 MB part. The scan had been longer (a leftover comment in the same file still says &lt;code&gt;recording (15s)&lt;/code&gt;). We cut it to ten to make the upload fit.&lt;/p&gt;

&lt;p&gt;That trade looked free at 2 a.m. It is the most important line in this post, and the third clock explains why.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gemini 2.5 Flash, and the guardrails around it
&lt;/h3&gt;

&lt;p&gt;The prompt is heavy. Gemini gets the full user profile, the vitals JSON, the readiness score and band, the list of injuries to work around, the equipment, the allergens (marked &lt;code&gt;STRICT&lt;/code&gt;), and the full exercise catalog with the instruction &lt;em&gt;use ONLY these, EXACT names&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A prompt is a request, though, not a guarantee. So every plan goes through &lt;code&gt;validateAndNormalizeWorkoutPlan()&lt;/code&gt;, which throws on the first violation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Error it throws (verbatim)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Exactly 7 days&lt;/td&gt;
&lt;td&gt;&lt;code&gt;workout_plan must contain exactly 7 days.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2-4 exercises per day&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Day 3 must contain 2-4 exercises, got 5.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Only exercises from the catalog&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Day 2, exercise 1 uses unknown exercise: "..."&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;No duplicates within a day&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Day 4 has duplicate exercise "..."&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;No exercise on consecutive days&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Day 5 repeats "..." from the previous day. Consecutive-day repeats are not allowed.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;At most 2 rest or recovery days&lt;/td&gt;
&lt;td&gt;&lt;code&gt;workout_plan has too many recovery/rest days (3). Max allowed is 2.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Enough variety across the week&lt;/td&gt;
&lt;td&gt;&lt;code&gt;workout_plan has low variety: 3 unique exercises, need at least 4.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Rule 7 is smarter than it looks. The variety floor is &lt;code&gt;max(4, min(10, availableCount))&lt;/code&gt;, where &lt;code&gt;availableCount&lt;/code&gt; is how many catalog exercises this user can actually do with their equipment and injuries. A bodyweight-only user with a bad knee cannot be asked for ten unique exercises. The comment in the code explains that without the cap, &lt;em&gt;"every plan, including the deterministic fallback, [would] fail validation."&lt;/em&gt; Somebody found that out the hard way.&lt;/p&gt;

&lt;p&gt;The chain around the validator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gemini (primary key -&amp;gt; backup key) -&amp;gt; Groq gpt-oss-120b if Gemini fails
  -&amp;gt; parse (with truncated-JSON recovery)
  -&amp;gt; validate
     -&amp;gt; fail: repair prompt containing the exact error -&amp;gt; parse -&amp;gt; validate
        -&amp;gt; fail: deterministic fallback workout -&amp;gt; validate
  -&amp;gt; tune for equipment + injuries -&amp;gt; validate again
  -&amp;gt; diet normalisation -&amp;gt; allergen check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is one generation, one targeted repair, then code. Not a retry loop that hopes for the best.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The elimination test.&lt;/strong&gt; Could Gemini be replaced by a template? Try this user: vegan, peanut allergy, resistance bands only, knee injury, high stress this morning. A template needs a branch for every combination of those. The LLM handles the combinatorics. The validator makes sure the combinatorics came back in a shape we can trust. &lt;strong&gt;An LLM without guardrails is just a template with extra steps. The guardrails are the engineering.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Groq
&lt;/h3&gt;

&lt;p&gt;Meal swaps only, and only for speed. &lt;code&gt;llama-3.1-8b-instant&lt;/code&gt;, &lt;code&gt;temperature: 0.2&lt;/code&gt;, JSON mode with a retry without it, 550 max tokens, and Gemini as the fallback. One job, done fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  MongoDB Atlas
&lt;/h3&gt;

&lt;p&gt;The social half of the System. An Express server exposes &lt;code&gt;GET /leaderboard&lt;/code&gt;, sorted &lt;code&gt;{ xp: -1, streak: -1 }&lt;/code&gt; (XP first, streak as the tiebreak), and &lt;code&gt;GET&lt;/code&gt;/&lt;code&gt;POST /community/posts&lt;/code&gt;, sorted newest first, with image upload. If Atlas can't be reached, the server swaps in an in-memory collection seeded with demo users, so the screen never goes blank on stage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why none of this is decoration
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Why it isn't decoration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Camera vitals&lt;/td&gt;
&lt;td&gt;Presage REST API&lt;/td&gt;
&lt;td&gt;Replace it with a "how do you feel?" slider and you've rebuilt every other fitness app. The scan &lt;em&gt;is&lt;/em&gt; the thesis.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan generation&lt;/td&gt;
&lt;td&gt;Gemini 2.5 Flash + Groq fallback&lt;/td&gt;
&lt;td&gt;Combinations of constraints no template covers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan validation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;validateAndNormalizeWorkoutPlan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Turns a probabilistic output into a guaranteed shape&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meal swap&lt;/td&gt;
&lt;td&gt;Groq &lt;code&gt;llama-3.1-8b-instant&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Latency budget different from plan generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Leaderboard + community&lt;/td&gt;
&lt;td&gt;MongoDB Atlas + Express&lt;/td&gt;
&lt;td&gt;Quests without a scoreboard are just chores&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  One scan, all the way down
&lt;/h2&gt;

&lt;p&gt;Let's go down through the layers of a single scan. This is a &lt;strong&gt;worked example with plausible values&lt;/strong&gt;, not a saved reading from the event. Every arithmetic step uses the real formula from &lt;a href="https://github.com/saviour2/solofit/blob/main/src/utils/readinessEngine.js" rel="noopener noreferrer"&gt;&lt;code&gt;readinessEngine.js&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Where it comes from&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Video&lt;/td&gt;
&lt;td&gt;10 s, 480p, muted, ~3 MB&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ScanScreen.js&lt;/code&gt;, in-code estimate 2-4 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upload parts&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3 MB fits in one 5 MB chunk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;id&lt;/code&gt; from &lt;code&gt;/v1/upload-url&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Presage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HR samples&lt;/td&gt;
&lt;td&gt;a time series keyed by seconds&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/retrieve-data&lt;/code&gt;, status &lt;code&gt;200&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Averaged HR&lt;/td&gt;
&lt;td&gt;88 bpm&lt;/td&gt;
&lt;td&gt;mean of every positive sample&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Averaged RR&lt;/td&gt;
&lt;td&gt;20 br/min&lt;/td&gt;
&lt;td&gt;mean of every positive sample&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stress&lt;/td&gt;
&lt;td&gt;&lt;code&gt;medium&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;inferred: 88 &amp;gt; 72&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source&lt;/td&gt;
&lt;td&gt;&lt;code&gt;presage_api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;green badge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Readiness&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;68&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;decomposed below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Band&lt;/td&gt;
&lt;td&gt;&lt;code&gt;light workout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;45-74&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan&lt;/td&gt;
&lt;td&gt;7 days, lighter volume&lt;/td&gt;
&lt;td&gt;Gemini, told &lt;code&gt;RECOMMENDATION: light workout&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation&lt;/td&gt;
&lt;td&gt;passes 7/7&lt;/td&gt;
&lt;td&gt;or repair, or fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now the score by hand, for a 19-year-old with no declared health issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;start                                             100
HR 88 &amp;gt; 80        excess 8, min(8 x 2, 30)        -16
HR &amp;gt; 0.7 x (220 - 19) = 140.7 ?   no                0
RR 20 &amp;gt; 18        (20 - 18) x 3                    -6
stress medium                                     -10
health issues     none                              0
                                                  ----
readiness                                          68   -&amp;gt;  "light workout"
&lt;/code&gt;&lt;/pre&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%2Fcuc55nf4orkxx69loje4.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%2Fcuc55nf4orkxx69loje4.png" alt="Waterfall chart: 100, minus 16 for heart rate, minus 6 for breathing, minus 10 for medium stress, ending at 68 in the light workout band" width="799" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The step to watch is the breathing term. Two breaths per minute of RR moved the score by 6 points. RR is also the number the third clock is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hackathon Experience
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLOCK 2 · THE BUILD · 36 HOURS&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;HackTropica 2K26. Asansol Engineering College, West Bengal. April 4-5, 2026. Thirty-six hours.&lt;/p&gt;

&lt;p&gt;The theme was &lt;strong&gt;Nature, and how beautiful it is.&lt;/strong&gt; We built an app that stares at your face. That sounds like the opposite of the brief until you remember the first paragraph of this post: a pulse is the oldest rhythm in nature, and it's been running under your skin your whole life. We just pointed a camera at it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;That's the event, not the product. The app demo is up in the Demo section. This is what the 36 hours looked like from outside the laptop.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The crew
&lt;/h3&gt;

&lt;p&gt;We called ourselves &lt;strong&gt;Kolkatar Rosogollas&lt;/strong&gt;, after Kolkata's most famous syrup-soaked sweet, and then spent a weekend building an app that writes people diet plans. I'll let that sit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Saikat Das&lt;/strong&gt; built the Presage connection: the upload, the S3 chunks, the polling, the parsing. It was the one piece nobody else on the team could have done, and the whole app stands on it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Me (Subarna Maity)&lt;/strong&gt; built the UI of the Expo app, including the redesign you're about to read about, plus the posting features and a steady stream of bug fixes as everything else broke around him.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soumyadeep Dey&lt;/strong&gt; Community posting, the onboarding flow that collects everything the prompt needs (age, goals, equipment, injuries, diet type, allergies, budget, cuisine), and the diet planning feature. So finding #6 further down is in my part of the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sriz Debnath&lt;/strong&gt; helped where he could, and spent much of the rest of the 36 hours on dedicated fieldwork into the food and the swag. He reports that both were excellent. Every team needs one person who remembers there's an event going on outside the terminal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A note on reading the repo: every commit is under Saikat's account, so the log tells you &lt;em&gt;when&lt;/em&gt; things happened, not &lt;em&gt;who&lt;/em&gt; did them. Treat it as the team's diary, not a scorecard.&lt;/p&gt;

&lt;h3&gt;
  
  
  The costume came off
&lt;/h3&gt;

&lt;p&gt;The biggest challenge of the weekend wasn't rPPG, or S3, or getting JSON out of an LLM. It was a question.&lt;/p&gt;

&lt;p&gt;We had started out building the app to look like the System itself: glowing blue quest windows, notification panels, the whole anime interface, as if the phone were Jinwoo's status screen. We loved it. It was the reason the name was SoloFit.&lt;/p&gt;

&lt;p&gt;Then the judges walked up to our table, looked at it, and asked something like: &lt;em&gt;if a serious fitness person opens this app, will they stay once they see an anime theme?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We didn't have a good answer, because there isn't one. Someone who trains every morning wants their numbers, not a costume. A theme that makes four hackers grin can make the person the app is actually for close it in five seconds. That hurt more than any stack trace that weekend, because it wasn't a bug. It was taste, and the judges were right.&lt;/p&gt;

&lt;p&gt;So we tore it down in the middle of the hackathon and rebuilt it as &lt;strong&gt;minimalist neo-brutalism&lt;/strong&gt;. Every colour is now in &lt;a href="https://github.com/saviour2/solofit/blob/main/src/constants/theme.js" rel="noopener noreferrer"&gt;&lt;code&gt;theme.js&lt;/code&gt;&lt;/a&gt;: a warm grey &lt;code&gt;#E8E8E6&lt;/code&gt; background, near-black &lt;code&gt;#111111&lt;/code&gt; text, one loud orange &lt;code&gt;#FF5A1F&lt;/code&gt; for anything you're meant to press, hard 1.5-2px borders, no glow anywhere. Heart rate, breathing and stress each get a single flat colour of their own. It's quiet enough for a serious lifter and blunt enough to still feel like a System.&lt;/p&gt;

&lt;p&gt;What we kept were the bones: quests, XP, the leaderboard, "Arise." What we threw away was the paint. That rebuild is where &lt;code&gt;Version 2.0.0 : Massive UI overhaul&lt;/code&gt; in the log below comes from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The anime was for us. The app was for them.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The diary
&lt;/h3&gt;

&lt;p&gt;Here's the diary, in IST:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Commit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apr 4, 19:10&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Solofit: AI-powered adaptive fitness coach app&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apr 4, 19:15&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chore: Secure API keys and move Firebase config to environment variables&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apr 4, 19:18&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Updated gitignore&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;five and a half hours of silence&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Apr 5, 00:54&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Presage implementation successful along with custom fallbacks&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apr 5, 01:00&lt;/td&gt;
&lt;td&gt;&lt;code&gt;feat: dynamic exercise GIFs with chevron dropdown in WorkoutScreen&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apr 5, 03:05&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Harden env safety and stabilize AI workout generation&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apr 5, 03:23&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Version 2.0.0 : Massive UI overhaul&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apr 5, 06:20&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3.0.0 : Leader board and Community sections live using Mongo DB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apr 5, 10:10&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2.6.0 : UI changes and AI model fallbacks added&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apr 5, 12:13&lt;/td&gt;
&lt;td&gt;&lt;code&gt;version 3.0.1 : Minor changes&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%2Fde029m22ac2g0i7x9vip.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%2Fde029m22ac2g0i7x9vip.png" alt="Commit timeline from 19:10 to 12:13 with a 5 hour 36 minute gap before the highlighted 00:54 Presage commit" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look at the gap. Between 19:18 and 00:54 there isn't a single commit. Those were Saikat's five and a half hours, and the commit that ends them is the one the whole project depends on. Our Devfolio "challenges" field sums up those hours in one line: &lt;em&gt;"Implementing presage SDK into React Native App but we nailed It."&lt;/em&gt; The Devfolio stack also lists C++ and CMake, which are not what you reach for when calling a REST API.&lt;/p&gt;

&lt;p&gt;The rest of the log reads like a team that has stopped being scared. Six minutes after Presage lands, someone ships exercise GIFs. By 03:23 the costume is off and the neo-brutalist UI is in. By 06:20 the leaderboard is live on Mongo. At 10:10 the version number goes &lt;em&gt;backwards&lt;/em&gt;, from 3.0.0 to 2.6.0, in a commit that adds AI model fallbacks. That tells you exactly how much sleep was involved.&lt;/p&gt;

&lt;p&gt;Look at the 00:54 commit message again, too. Not "Presage implementation successful." It's &lt;strong&gt;"Presage implementation successful along with custom fallbacks."&lt;/strong&gt; The fallback was born in the same minute as the feature. Hold on to that.&lt;/p&gt;

&lt;p&gt;Then judging, and the prize: &lt;strong&gt;MLH: Best Use of Presage SDK.&lt;/strong&gt; We also applied to the MongoDB and Gemini tracks. We didn't win those. We won the one the whole app was built around.&lt;/p&gt;

&lt;h2&gt;
  
  
  [Warning] What I'd fix before anyone trains on this
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLOCK 3 · THE READING · 5 MONTHS LATER&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;[Warning]&lt;/strong&gt; Vitals estimated. Intense quests should be locked. They aren't.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Five months later, sitting down to write this post, I opened Presage's current SmartSpectra docs to get one sentence right about breathing rate. This is the sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Confidence is 0 until the full 30-second window is reached.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Our scan is 10 seconds long.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go back through everything above with that in mind. The upload limit that pushed the scan down to ten seconds. The parse step that averages every sample and never checks confidence. The readiness formula where two breaths per minute cost six points. None of it was wrong at 2 a.m. It was just built without the one number that tells you whether the breathing data can be trusted at all.&lt;/p&gt;

&lt;p&gt;These are findings, not apologies. Each one comes with its fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Breathing rate from a 10-second window.&lt;/strong&gt; Presage's docs say breathing-rate confidence stays at 0 until 30 seconds. (Those docs describe the SDK. I haven't confirmed whether the REST endpoint behaves exactly the same.) Either way, a breathing rate from ten seconds of video is a number the provider itself says not to lean on, and our score leans on it.&lt;br&gt;
&lt;em&gt;Fix:&lt;/em&gt; On scans under 30 seconds, drop RR from the score completely. When breathing matters, scan for 30 seconds and keep the file under the limit by dropping to a lower resolution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Averaging blindly.&lt;/strong&gt; &lt;code&gt;parseResults()&lt;/code&gt; averages every positive sample. The docs list what degrades the signal: large head, body or camera motion, chewing gum, flickering light like a TV screen. Pulse is valid for roughly 40-110 BPM.&lt;br&gt;
&lt;em&gt;Fix:&lt;/em&gt; If the API returns confidence with each sample, weight by it or throw away low-confidence samples. Either way, clamp HR to the valid range before it gets near a score.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A hard-coded breathing rate labelled as live data.&lt;/strong&gt; Look at &lt;code&gt;rr ?? 15&lt;/code&gt; again. If Presage sends back heart rate but no usable breathing rate, the app puts in 15, and the object still says &lt;code&gt;source: "presage_api"&lt;/code&gt;. The green badge lights up for a number nobody measured.&lt;br&gt;
&lt;em&gt;Fix:&lt;/em&gt; Return &lt;code&gt;breathing_rate: null&lt;/code&gt; and track source per field, not per object.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The fallback can unlock an intense workout.&lt;/strong&gt; This is the big one. Here are lines 62-65 of &lt;code&gt;readinessEngine.js&lt;/code&gt;:&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="c1"&gt;// For estimated/mock vitals, keep readiness randomized strictly within 55-85.&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isFallbackSource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;55&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;In plain English: if the vitals were made up, make the score up too, somewhere between 55 and 85. But 75 and above maps to &lt;code&gt;"intense workout"&lt;/code&gt;. From 55 to 85 there are 31 possible scores, and 11 of them are 75 or higher. So &lt;strong&gt;on a fallback scan, there's roughly a 35% chance the app prescribes an intense session from vitals that were never measured.&lt;/strong&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%2Fdtwsu5h03c78wkjxxq2d.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%2Fdtwsu5h03c78wkjxxq2d.png" alt="31 boxes numbered 55 to 85, with 75 to 85 highlighted as intense workout: 11 of 31, about 35%" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I didn't have to imagine this. I ran into it while taking the screenshots for this post. Chromium's fake webcam produced no usable video file, so the app returned &lt;code&gt;mock_no_video&lt;/code&gt;, and the random roll landed on 78:&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%2Foq5yari7896rznik9oxd.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%2Foq5yari7896rznik9oxd.png" alt="Three real screens: Estimated Vitals with readiness 78 and Intense Workout, the dashboard saying READY, and the workout tab reading Readiness 78/100 intense workout with upper body hypertrophy on day 1" width="800" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The amber badge was there, just like it's supposed to be. So were a green 78, a "READY" pill and an upper-body hypertrophy session, all built on a heartbeat nobody measured. It gets worse downstream: the vitals JSON goes into the Gemini prompt under the heading &lt;code&gt;REAL-TIME BODY DATA&lt;/code&gt;. The &lt;code&gt;source&lt;/code&gt; field is inside that JSON, but nothing tells the model what &lt;code&gt;mock_api_fallback&lt;/code&gt; means.&lt;/p&gt;

&lt;p&gt;The fallback was a good decision for a demo. The mistake was letting it go further than the badge.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fix:&lt;/em&gt;&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isFallbackSource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;readiness_score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;recommendation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light workout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;details&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vitals were estimated, not measured. Intense quests stay locked until a real scan.&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In plain English: when we don't know, say so, and pick the safe option. Estimated vitals can unlock a light day. They should never unlock a hard one. Tell the model the same thing in the prompt, and make the amber badge impossible to miss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Two different definitions of "high stress".&lt;/strong&gt; Real vitals call stress high above 90 bpm. The mock generator uses above 85. That's small, but it means the same heart rate gets a different stress label depending on where it came from.&lt;br&gt;
&lt;em&gt;Fix:&lt;/em&gt; One &lt;code&gt;inferStress()&lt;/code&gt; function, used by both paths.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. The allergen check flags instead of blocking.&lt;/strong&gt; &lt;code&gt;validateNoAllergens()&lt;/code&gt; does a substring match on meal names and, on a hit, renames the meal &lt;code&gt;[ALLERGEN WARNING] ...&lt;/code&gt;. It doesn't reject the plan, and it can't catch a peanut-based satay that never uses the word "peanut."&lt;br&gt;
&lt;em&gt;Fix:&lt;/em&gt; Send a flagged plan back through the same repair loop the workout validator uses, and match against ingredients, not just names.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The magic trick was real. The part we never checked was how long it needs to run.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What this unlocks
&lt;/h2&gt;

&lt;p&gt;Solo Leveling was the costume, and the judges already made us take it off once. What was left underneath is one idea: &lt;strong&gt;measure the user before you personalise anything.&lt;/strong&gt; Nearly every "personalised" app personalises from a profile you filled in once. A camera-based vital sign is a profile that updates every morning.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Study planners&lt;/strong&gt; that check fatigue before scheduling the hard chapter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus timers&lt;/strong&gt; that stretch the break when your resting heart rate is up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rehab programmes&lt;/strong&gt; that won't progress the load on a day your body says no.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shift and driver apps&lt;/strong&gt; that ask for thirty seconds before a long night, not a checkbox.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of them gets the same three lessons from this post: tag the source, respect the window, and never let an estimate unlock the risky option.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do you measure heart rate from a phone camera in React Native?
&lt;/h3&gt;

&lt;p&gt;Record a short front-camera video with &lt;code&gt;expo-camera&lt;/code&gt; (&lt;code&gt;recordAsync&lt;/code&gt; with &lt;code&gt;quality: "480p"&lt;/code&gt; and &lt;code&gt;mute: true&lt;/code&gt;), then send it to an rPPG service. With the Presage Physiology REST API: &lt;code&gt;POST /v1/upload-url&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt; the chunks to the presigned S3 URLs, &lt;code&gt;POST /v1/complete&lt;/code&gt;, then poll &lt;code&gt;POST /retrieve-data&lt;/code&gt; until you get a &lt;code&gt;200&lt;/code&gt;. The response is a heart-rate time series you can summarise.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is rPPG and how accurate is it?
&lt;/h3&gt;

&lt;p&gt;Remote photoplethysmography reads the tiny colour changes in skin caused by blood flow. Presage's docs give a valid pulse range of about 40-110 BPM and list large motion, flickering light and chewing gum as things that degrade it. Its metrics are for general wellness, not FDA cleared, and not for diagnosis.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does a Presage scan need for breathing rate?
&lt;/h3&gt;

&lt;p&gt;Presage's SmartSpectra docs say breathing-rate confidence is 0 until a full 30-second window, and HRV statistics need 60 seconds. A 10-second scan is fine for a pulse estimate. It isn't enough to trust breathing rate.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you stop an LLM from returning an invalid workout plan?
&lt;/h3&gt;

&lt;p&gt;Don't rely on the prompt. Validate the parsed JSON against hard rules in code (for us: 7 days, 2-4 exercises per day, catalog-only names, no duplicates in a day, no consecutive-day repeats, at most 2 rest days, a variety floor). On failure, send the exact error back in a repair prompt. If the repair fails too, build the plan deterministically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does my Presage poll keep returning 201?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;201&lt;/code&gt; from &lt;code&gt;/retrieve-data&lt;/code&gt; means the job is still processing, not that it failed. Keep polling. We used a 2.5-second interval and gave up after 40 attempts (100 seconds), which is when &lt;code&gt;Presage processing timed out&lt;/code&gt; fires and the fallback kicks in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Team Kolkatar Rosogollas:&lt;/strong&gt; Saikat Das (the Presage pipeline), Subarna Maity (the UI, posting and bug fixes), Soumyadeep Dey (posting, onboarding, diet planning), Sriz Debnath (morale, food reconnaissance, swag).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HackTropica 2K26&lt;/strong&gt; and Asansol Engineering College, for the 36 hours and a Nature theme we stretched as far as it would go.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The judges&lt;/strong&gt;, for the question that killed our anime UI. It was the best feedback we got all weekend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MLH&lt;/strong&gt;, for the Presage track and the prize.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presage&lt;/strong&gt;, for an API that turned "read a pulse from a face" into five HTTP calls, and for docs honest enough to tell us where we went wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solo Leveling&lt;/strong&gt; by Chugong, for the System we wanted to argue with.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;SoloFit is a general-wellness fitness app. Its vitals are not medical measurements, and nothing in it or in this post replaces advice from a doctor. This post was drafted with AI assistance; the code, the build and the mistakes are ours.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mlhacks</category>
      <category>devchallenge</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>Feels Great To be part of this project, its a awesome experince to build this project and completed it on time</title>
      <dc:creator>Subarna Maity</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:18:31 +0000</pubDate>
      <link>https://dev.to/dronzer2code/feels-great-to-be-part-of-this-project-its-a-awesome-experince-to-build-this-project-and-completed-4kl6</link>
      <guid>https://dev.to/dronzer2code/feels-great-to-be-part-of-this-project-its-a-awesome-experince-to-build-this-project-and-completed-4kl6</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/soumyadeepdey/we-taught-a-snowflake-warehouse-to-judge-world-cup-conviction-and-write-the-verdict-back-to-solana-305i" class="crayons-story__hidden-navigation-link"&gt;We Taught a Snowflake Warehouse to Judge World Cup Conviction and Write the Verdict Back to Solana&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/soumyadeepdey/we-taught-a-snowflake-warehouse-to-judge-world-cup-conviction-and-write-the-verdict-back-to-solana-305i" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;DEV Weekend Challenge: Passion Edition Submission&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/soumyadeepdey" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F1076361%2F0aa8630b-3826-4103-9ca3-7436013bf933.jpg" alt="soumyadeepdey profile" class="crayons-avatar__image" width="460" height="460"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/soumyadeepdey" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Soumyadeep Dey 
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Soumyadeep Dey 
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png" width="166" height="102"&gt;&lt;/a&gt;
                
              
              &lt;div id="story-author-preview-content-4127328" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/soumyadeepdey" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F1076361%2F0aa8630b-3826-4103-9ca3-7436013bf933.jpg" class="crayons-avatar__image" alt="" width="460" height="460"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Soumyadeep Dey &lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/soumyadeepdey/we-taught-a-snowflake-warehouse-to-judge-world-cup-conviction-and-write-the-verdict-back-to-solana-305i" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 12&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/soumyadeepdey/we-taught-a-snowflake-warehouse-to-judge-world-cup-conviction-and-write-the-verdict-back-to-solana-305i" id="article-link-4127328"&gt;
          We Taught a Snowflake Warehouse to Judge World Cup Conviction and Write the Verdict Back to Solana
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/weekendchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;weekendchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/snowflake"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;snowflake&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/100daysofsolana"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;100daysofsolana&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/soumyadeepdey/we-taught-a-snowflake-warehouse-to-judge-world-cup-conviction-and-write-the-verdict-back-to-solana-305i" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;32&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/soumyadeepdey/we-taught-a-snowflake-warehouse-to-judge-world-cup-conviction-and-write-the-verdict-back-to-solana-305i#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              4&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            15 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>blockchain</category>
      <category>database</category>
      <category>dataengineering</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
