<?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: Sohee Lee </title>
    <description>The latest articles on DEV Community by Sohee Lee  (@gracelee087).</description>
    <link>https://dev.to/gracelee087</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%2F3748216%2F0edc4de4-8ddd-442b-9da0-c2914fe4202d.jpg</url>
      <title>DEV Community: Sohee Lee </title>
      <link>https://dev.to/gracelee087</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gracelee087"/>
    <language>en</language>
    <item>
      <title>knowledge-gate: slow and steady, a new AI talk becomes one line in my notes</title>
      <dc:creator>Sohee Lee </dc:creator>
      <pubDate>Sat, 24 Oct 2026 21:25:26 +0000</pubDate>
      <link>https://dev.to/gracelee087/knowledge-gate-ai-talks-into-my-own-notes-while-i-sleep-1agg</link>
      <guid>https://dev.to/gracelee087/knowledge-gate-ai-talks-into-my-own-notes-while-i-sleep-1agg</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I learn from AI engineering talks on YouTube, and I keep my own notes on the ideas that matter. The talks win. One channel I follow posted 77 videos in 30 days, 30.6 hours of video, and many of them say the same thing under different names.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;knowledge-gate&lt;/strong&gt; watches the channels for me, slowly and steadily. When a talk is uploaded, a small server turns it into a &lt;strong&gt;card&lt;/strong&gt;: a one-line conclusion, a short summary, and claims, each with a word-for-word quote and the second it was said. The cards grow into one map of concepts, so ten talks about one idea become one page. When I open my laptop, Gemma 4 12B in Ollama reads the new cards next to my notes and appends the points they don't have yet, below what I wrote. The notes themselves stay on the laptop.&lt;/p&gt;

&lt;p&gt;So far (evening of October 24): 95 talks on the map, 444 claims, and 397 of their quotes (89%) found word for word in the transcript, ignoring case, punctuation and "uh". Only those are ever shown. This checks that the speaker said it, not that the claim is right.&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%2Fbstg7f0q46dr18mnms1u.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%2Fbstg7f0q46dr18mnms1u.png" alt="knowledge-gate flow" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Before, a new talk would pop up and I'd think: I'll watch it later. Then I got busy with other things and forgot, and the ones I did watch I had to sum up and copy into my notes by hand. Now that is the server's and the laptop's job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I used it this week.&lt;/strong&gt; On October 21 the server took five uploads from push notifications without me: four became cards, and one narrated in Korean was skipped on purpose. On October 24 it happened again with an Arize AI talk, "Your Agent Fix Just Broke Something Else". Its push notification reached the server at 19:01 UTC, the card was made without me, and that evening the merge put one point into my Agent Harness note: 46 lines became 48, the git diff only adds lines, and the new point links to 05:43 in the talk.&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%2F3a6wkchjacxoxp1xu5fo.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%2F3a6wkchjacxoxp1xu5fo.png" alt="My Agent Harness note on the laptop page, with the Oct 24 point from the Arize AI talk marked NEW" width="800" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My Note → Agent Harness on the laptop page (127.0.0.1:4321), captured October 25. My working definition and open questions sit above; the highlighted point is the one merged on October 24.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It still works without me, end to end.&lt;/strong&gt; On October 24 I checked the whole path once more, from a YouTube upload to an answer, without starting anything by hand on the server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;15:00 UTC: the AI Engineer channel uploaded "How Bridgewater Turned 100 Million Documents Into an Edge".&lt;/li&gt;
&lt;li&gt;15:01: YouTube's push notification reached the webhook, and the workflow started.&lt;/li&gt;
&lt;li&gt;15:04: Temporal marked it completed: 132 transcript lines, a card, 2 new concepts on the map, filed under Memory &amp;amp; Context, all checks passed.&lt;/li&gt;
&lt;li&gt;That evening, the laptop's morning step pulled it and added 4 points from this talk (7 from that day's two talks) to my notes, each with its quote. The git check reported my existing text unchanged.&lt;/li&gt;
&lt;li&gt;I typed "What is native filtering in vector search?" and the Notes Assistant answered from that talk, with a link to 10:34, the second it was said.&lt;/li&gt;
&lt;li&gt;A second talk that evening, whose push notification arrived at 19:01 UTC, went through the server the same way. To check the laptop's scheduled morning task, I started it by hand; it pulled the talk and added 5 points, 2 of them to my own notes (Agent Harness, Code review with agents).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That check also found a bug of mine: four paper cards left over from an earlier experiment had no upload age, and the morning step stopped at the map. It is fixed, and the paper cards are out, because this project is about talks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it saves.&lt;/strong&gt; On October 22 the laptop page held 4 new talks: 1 hour 52 minutes of video, and about 7 minutes to read. The 7 minutes is the page's own estimate at 200 words a minute; I did not time myself. I still choose which talks to watch in full.&lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/3SoKm_N-4lU" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Video: &lt;a href="https://youtu.be/3SoKm_N-4lU" rel="noopener noreferrer"&gt;https://youtu.be/3SoKm_N-4lU&lt;/a&gt; (2:02)&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/gracelee087" rel="noopener noreferrer"&gt;
        gracelee087
      &lt;/a&gt; / &lt;a href="https://github.com/gracelee087/knowledge-gate" rel="noopener noreferrer"&gt;
        knowledge-gate
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      knowledge-gate: AI talks become checked cards and land in my own notes, by open Gemma models. Answered from my notes.
    &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;knowledge-gate&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Slow and steady: a small server turns every new AI engineering talk into a checked card, and Gemma 4 on my laptop
appends the points my notes don't have yet. The notes themselves stay on the laptop.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/gracelee087/knowledge-gate/docs/images/morning-page.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%2Fgracelee087%2Fknowledge-gate%2FHEAD%2Fdocs%2Fimages%2Fmorning-page.png" alt="The morning page on my laptop: today's new talks, each with a one-line conclusion, a summary with marked key phrases, and the &amp;quot;Ask your notes&amp;quot; panel"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The morning page in Chrome at &lt;code&gt;http://127.0.0.1:4321&lt;/code&gt; on my laptop (Oct 24), with the talks that arrived that day.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo video (2:02):&lt;/strong&gt; &lt;a href="https://youtu.be/3SoKm_N-4lU" rel="nofollow noopener noreferrer"&gt;https://youtu.be/3SoKm_N-4lU&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;At a glance&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it does&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A new AI talk is uploaded → the server turns it into a checked card → every morning my laptop adds only what is new to my own notes → I can ask, by typing or by voice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gemma 4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gemma 4 31B writes the cards and joins concepts on the server; Gemma 4 12B and EmbeddingGemma in Ollama merge my notes and answer on the laptop. &lt;a href="https://github.com/gracelee087/knowledge-gate#the-ai-agents-one-job-each" rel="noopener noreferrer"&gt;The AI agents&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Checked by code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A quote is kept only if it is in the&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/gracelee087/knowledge-gate" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Repository: &lt;a href="https://github.com/gracelee087/knowledge-gate" rel="noopener noreferrer"&gt;https://github.com/gracelee087/knowledge-gate&lt;/a&gt; (MIT)&lt;/p&gt;

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

&lt;h3&gt;
  
  
  A model writes, code checks
&lt;/h3&gt;

&lt;p&gt;My first try was the obvious one: give a small model the tools and let it run the job. Gemma 4 E4B, as a Mastra agent with two tools, called the transcript tool once, never called the notes tool, and then asked me for the video ID it had just used. So no model chooses the steps. The pipeline is one fixed Mastra workflow on Temporal, each model call has one job, and code checks every answer before it is kept.&lt;/p&gt;

&lt;p&gt;Every agent is named after its job:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;agent&lt;/th&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;th&gt;job&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Card Writer&lt;/td&gt;
&lt;td&gt;Gemma 4 31B&lt;/td&gt;
&lt;td&gt;writes the card&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quote Checker&lt;/td&gt;
&lt;td&gt;none, plain code&lt;/td&gt;
&lt;td&gt;keeps a quote only if it is in the transcript word for word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concept Matcher&lt;/td&gt;
&lt;td&gt;Gemma 4 31B + bge-m3&lt;/td&gt;
&lt;td&gt;same idea as an earlier talk, or new?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Category Keeper&lt;/td&gt;
&lt;td&gt;Gemma 4 31B&lt;/td&gt;
&lt;td&gt;files each talk in a section&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rule Tester&lt;/td&gt;
&lt;td&gt;Gemma 4 31B&lt;/td&gt;
&lt;td&gt;keeps a rule only if runs with it are as good or better&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Note Merger&lt;/td&gt;
&lt;td&gt;Gemma 4 12B + EmbeddingGemma&lt;/td&gt;
&lt;td&gt;adds new points to my notes, on the laptop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Highlighter&lt;/td&gt;
&lt;td&gt;Gemma 4 12B&lt;/td&gt;
&lt;td&gt;marks key phrases, on the laptop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notes Assistant&lt;/td&gt;
&lt;td&gt;Gemma 4 12B + EmbeddingGemma&lt;/td&gt;
&lt;td&gt;answers my questions from the talks and my notes, on the laptop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Gemma 4 31B and bge-m3 run on DigitalOcean's serverless inference. The laptop agents run in Ollama.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worst-case scenarios while running
&lt;/h3&gt;

&lt;p&gt;While building it, I kept asking one question: what is the worst thing this could do while I sleep? Each answer got a check, and each check has met the real thing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What if…&lt;/th&gt;
&lt;th&gt;What catches it&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;a card quotes something the speaker never said?&lt;/td&gt;
&lt;td&gt;The Quote Checker keeps a quote only if it is in the transcript word for word.&lt;/td&gt;
&lt;td&gt;397 of 444 quotes found (Oct 24); only those are ever shown.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the merge changes or deletes my own words?&lt;/td&gt;
&lt;td&gt;The merge only appends. After every merge a git check reports any existing line that was removed or changed. A claim my note already has word for word is marked known before the model is asked.&lt;/td&gt;
&lt;td&gt;Oct 24: the Arize AI talk took my Agent Harness note from 46 to 48 lines, and the git diff of that merge only adds lines. Merging the same 4 talks a second time: 23 of 23 known, 0 appended.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the server dies in the middle of a talk?&lt;/td&gt;
&lt;td&gt;Temporal: every step has a 5-minute timeout and up to 3 attempts.&lt;/td&gt;
&lt;td&gt;I killed the worker during the map step; only that step ran again, with the same result. On Oct 22, for real: the card step timed out once, and Attempt 2 finished it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;two different ideas are merged because they share a word?&lt;/td&gt;
&lt;td&gt;Atlas vector search gives 6 candidates, code accepts only an answer that names one, and a second, strict call checks every join.&lt;/td&gt;
&lt;td&gt;"AI Factory" (data centers) was pulled out of "Software Factory".&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;something fails and nobody notices?&lt;/td&gt;
&lt;td&gt;Sentry records each failed step as an issue.&lt;/td&gt;
&lt;td&gt;LECTURE-GATE-4: the map step failed with &lt;code&gt;EACCES&lt;/code&gt;, and the laptop had 77 cards while the server had 80.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the laptop's morning step fails quietly?&lt;/td&gt;
&lt;td&gt;Task Scheduler records the exit code, and the morning log keeps the error. No alarm on the laptop yet.&lt;/td&gt;
&lt;td&gt;Oct 24: my end-to-end check, not an alarm, found that the morning task still ran an old copy of the project and failed (result 1 at 10:09), and that the morning step stopped at the map on four leftover paper cards. Both fixed; the next run ended with result 0.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The one that surprised me.&lt;/strong&gt; On its first test the Quote Checker rejected 2 of 4 real quotes. The model was not the problem: auto-captions contain "uh"s, and the model had dropped them. Fillers, case and punctuation are now ignored on both sides. A check that is too strict looks safe, but it throws away true quotes.&lt;/p&gt;

&lt;p&gt;Two smaller ones were Gemma's own. On one real card it wrote the chapter summaries in Korean; a code check now finds non-English fields and sends the card back with "Translate these fields into English … Change nothing else.", which fixed it 3 times in 3 (making the card again from scratch: 1 in 3). And it borrowed the 4 example concept names from my prompt, 18 times across all cards; after I removed them, the share of concept names in the speaker's own words went from 95% to 99%.&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%2Fndjs2yfjwt5vho6w0jg4.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%2Fndjs2yfjwt5vho6w0jg4.png" alt="A atlas vec index dev" width="800" height="644"&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%2Fwi99xs2ykpsg1bktbmf4.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%2Fwi99xs2ykpsg1bktbmf4.png" alt="A8 atlas vectors dev" width="697" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The real vectors in Atlas &lt;code&gt;lecture_gate.mentions&lt;/code&gt;: one talk’s 5 concepts, each stored as 1024 numbers from bge-m3 (first 6 shown). The &lt;code&gt;vec&lt;/code&gt; index above compares them by cosine.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Measured, not assumed
&lt;/h3&gt;

&lt;p&gt;I picked the card model on a test set of 10 talks (8 to 46 minutes, talks and interviews). With my first prompt, Gemma 4 31B found 90% of its quotes and gpt-oss-120b 71%, at $0.0018 against $0.0021 per card. After that, the prompt changed only when the same test set said so. From v2 to v7, quotes found rose from 88% to 96%. v8, live since October 22, adds the summary and one claim per 5 minutes of talk; quotes found fell to 94.2%.&lt;/p&gt;

&lt;p&gt;Could the laptop make the cards itself? On its CPU (no GPU), one card each for two talks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;model in Ollama&lt;/th&gt;
&lt;th&gt;quotes found&lt;/th&gt;
&lt;th&gt;time per card&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 E2B&lt;/td&gt;
&lt;td&gt;8/9&lt;/td&gt;
&lt;td&gt;3.7–5.6 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 E4B&lt;/td&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;td&gt;8.0–10.0 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 12B&lt;/td&gt;
&lt;td&gt;8/8&lt;/td&gt;
&lt;td&gt;20.4–21.8 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 3 4B&lt;/td&gt;
&lt;td&gt;1/7&lt;/td&gt;
&lt;td&gt;9.7–11.3 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3 8B&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;19.2–33.7 min&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%2F5z83ecsxvrlhbnybd7kl.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%2F5z83ecsxvrlhbnybd7kl.png" alt="E13 sentry local models dev" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That ranks the models; it proves nothing. Minutes per card is too slow, so cards are made in the cloud and the laptop gets the private job. For that job, E4B handled 17 claims in 86 s against 290 s for 12B, but appended 4 claims my notes already said. A merge that repeats my notes is worse than a slow one, so the Note Merger runs 12B.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistakes become rules, and a rule has to earn its place
&lt;/h3&gt;

&lt;p&gt;Every failed check is saved as a lesson. Showing the agent its own past mistakes did not help: the Category Keeper failed all 4 rounds the same way. So the Rule Tester turns lessons into a candidate rule and runs the task with and without it: 3 runs each for a category rule; for a card rule, one first attempt on the talks the lessons came from plus 2 test talks, where at least as many cards must pass and verbatim quotes may drop by no more than 10 points. A rule that does as well or better goes to Backboard, where the prompts read it. Only the rule reaches the model, never the lessons behind it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Do not group terms that share a keyword but refer to different functional roles…": broken checks went from [5, 14, 14] to [3, 2, 2], 79% fewer, in one manual run on October 21. Kept.&lt;/li&gt;
&lt;li&gt;"Put every lecture in one single category named All Lectures", a bad rule I fed it on purpose: [0, 1, 0] became [1, 1, 1]. Rejected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On October 22 Backboard held 7 rules. The newest was added that morning: "Do not group terms where one describes a high-level goal or purpose and the other describes a specific technical implementation or process."&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%2F4q8u9oi76urz20p67nbg.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%2F4q8u9oi76urz20p67nbg.png" alt="P backboard rules dev" width="799" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Slow and steady: I killed the worker on purpose
&lt;/h3&gt;

&lt;p&gt;To test it, I force-killed the worker 19 seconds into the map step, then started it again. Transcript and card did not run again. The map step's second attempt started about 5 minutes after it was scheduled and finished in 31 s, with the same result as the all-at-once run: "concepts 4/4 same; category same". The price is the wait: &lt;code&gt;@mastra/temporal&lt;/code&gt; has no heartbeat option, so Temporal only notices a dead worker when the timeout runs out.&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%2Fofmll3smf2gn5w24n0s1.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%2Fofmll3smf2gn5w24n0s1.png" alt="P mastra dev" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Mastra workflow definition (&lt;code&gt;src/mastra/index.ts&lt;/code&gt;): 5-minute timeout, 3 attempts, and three steps in a fixed order.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftj6q6mhrjsukal36wkws.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%2Ftj6q6mhrjsukal36wkws.png" alt="E28 kill timeline dev" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The October 22 retry from the table, as Temporal recorded it: the second attempt finished in under 3 minutes, and the whole run in 9, while I was doing something else.&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%2F2etn96ajfkz0ylnrttat.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%2F2etn96ajfkz0ylnrttat.png" alt="T temporal retry dev" width="745" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Every call traced, and one real incident
&lt;/h3&gt;

&lt;p&gt;Every model call is a Sentry &lt;code&gt;gen_ai.chat&lt;/code&gt; span with the model name, time and token counts. In the first 14 days (to October 22), Sentry recorded 977 model calls, across every model I compared, from Gemma 4 E2B on my laptop to gpt-oss-120b in the cloud. It prices each card call from its tokens (under $0.01), and the output column stays empty: prompts and outputs are never sent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F63nrzvuxmvxptt1jf7ip.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%2F63nrzvuxmvxptt1jf7ip.png" alt="S sentry llm calls dev" width="799" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;LECTURE-GATE-4 from the table was my own mistake: I had regenerated the map files as root, but the services run as an unprivileged user. A &lt;code&gt;chown&lt;/code&gt; and a re-run as the service user fixed it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2vob9u4zple9j108atfh.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%2F2vob9u4zple9j108atfh.png" alt="S sentry issue dev" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The base is cheap and boring. A $6 DigitalOcean Droplet runs the server and uses 833 of 961 MiB at idle. YouTube blocked scripted transcript fetches from my home and from the Droplet, so transcripts come from SerpApi, cached on disk because the free plan has 250 searches a month.&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%2F7ogozd27ykatvujg7ltp.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%2F7ogozd27ykatvujg7ltp.png" alt="S serpapi searches dev" width="800" height="746"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SerpApi search log: each row is one talk's transcript request (&lt;code&gt;youtube_video_transcript&lt;/code&gt;). The top row is the newest talk (&lt;code&gt;Ve2bAarPA2I&lt;/code&gt;), October 22, 19:01 UTC. 105 of the month's 250 searches used.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3hyky01hpjvjgdm05jl6.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%2F3hyky01hpjvjgdm05jl6.png" alt="DO cost compact" width="799" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Apps $1.48" on that bill (October 1–21) is another project of mine on the same account. This project is GenAI Serverless Inference $0.83 + Droplets $0.02.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  On my laptop: how the note grows
&lt;/h3&gt;

&lt;p&gt;The Note Merger reads each new claim next to the note it belongs to (EmbeddingGemma finds the note by meaning) and answers "known", "new" or "unrelated". "Known" counts only if the model copies the note sentence that already says it, and code checks that sentence is there. On the laptop page my notes live under &lt;strong&gt;My Note&lt;/strong&gt; in the sidebar.&lt;/p&gt;

&lt;p&gt;Points that none of my notes takes go to a note for their section of the map, so each section grows into a note of its own, without repeats. When a talk raises a question, the Notes Assistant answers only from the 8 closest claims and up to 2 notes, citing [C1] or [N1]; a citation that matches no source is not linked, and the page flags it. The page runs on 127.0.0.1 only.&lt;/p&gt;

&lt;p&gt;I can also ask by voice, but only if I start the page with &lt;code&gt;--stt whisper&lt;/code&gt;; it is off by default. Whisper runs on the laptop, but in my tests it heard "agent hardness" for "agent harness" and "head fund" for "hedge fund", and then my notes had no answer. Typing is slower (the 12B answer above took about 4 minutes on my CPU) but it hears me right.&lt;/p&gt;

&lt;p&gt;The demo's narration was made with ElevenLabs (voice "Lara"). Only the narration script was sent. The page also has an ElevenLabs demo mode that reads answers aloud; then the answer text goes to ElevenLabs, and the page says so in its header. By default it uses the browser's own voices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations, and what I want to try next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Most comparisons are small: at most 3 runs per side, and the local-model table is one run on two talks. The Rule Tester keeps a rule on "no harm", not on proven help.&lt;/li&gt;
&lt;li&gt;The Notes Assistant's conclusion can change between runs. The sources it shows are real; the conclusion is not stable.&lt;/li&gt;
&lt;li&gt;My notes in these tests are four sample notes, and the map covers English talks only. I did not measure how long reading the cards takes me, so I claim no hours saved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I want to try next, to catch more errors and make it faster: more harness (a code check behind every model decision, which also skips a model call that takes minutes on my CPU), more nightly Rule Tester runs so a rule is kept on measured help and not only "no harm", the same test set for the laptop agents with more runs per model, and a cost comparison of cloud against laptop, per agent.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Open worked better here because of my notes.&lt;/strong&gt; A hosted model would need my notes on someone else's server. With open weights, the model that reads them runs on my laptop, and after the morning pull from Atlas, the merge, the highlights and every answer come from Gemma in Ollama on 127.0.0.1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open models work for me while I sleep.&lt;/strong&gt; Talks arrive at any hour; the server turns them into cards, and every night the Rule Tester checks its new rules, without me. Nothing in that loop is specific to lectures: the same watch, check and merge could follow other sources I learn from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My notes stay mine.&lt;/strong&gt; They are not only about lectures: they hold what I study on my own, my schedule, and the questions I haven't figured out yet, my own map of what I know and what I don't. Only public talks go to the cloud, where they become the shared concept map below.&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%2F49e9gwyqztas36j0ob3t.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%2F49e9gwyqztas36j0ob3t.png" alt="map concept graph" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The concept map from public talks, at 72 lectures. Each dot is a concept, a line joins two concepts named in the same lecture, and the largest dot, Agent Harness, spans 11 lectures.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I learn from my own collection.&lt;/strong&gt; Because the notes and the cards sit on my laptop, I can ask the Notes Assistant right away in the chat, and it answers from the talks and my notes, citing both. Asking and reading the sources is how I build up the core ideas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same family, in two places.&lt;/strong&gt; Open weights let me split the work: Gemma 4 31B in the cloud for public talks, Gemma 4 12B on my CPU for private notes. Each was chosen by the measurements above. The cloud side cost $0.85 from October 1 to 21, so it runs every day without rationing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where a closed model might be better:&lt;/strong&gt; a large hosted model might write smoother cards with fewer retries. It would still need the same checks.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 4 31B writes cards, matches concepts, files talks and tests rules; Gemma 4 12B and EmbeddingGemma merge, highlight and answer on my laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of DigitalOcean&lt;/strong&gt;: serverless Gemma 4 31B and bge-m3, and the $6 Droplet that runs the whole server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Mastra&lt;/strong&gt;: one fixed three-step workflow instead of an agent choosing its own steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Temporal&lt;/strong&gt;: the workflow survived a killed worker, and only the killed step ran again; in production it retried a timed-out card step by itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of MongoDB Atlas&lt;/strong&gt;: vector search gives the 6 candidates for every new concept, and Atlas carries public cards down to the laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Sentry Agent Tracing&lt;/strong&gt;: every model call traced with time and tokens, no prompt or output; it caught incident LECTURE-GATE-4.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Backboard&lt;/strong&gt;: it holds only rules that matched or beat their own baseline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of SerpApi&lt;/strong&gt;: the transcript and chapter list behind every card, cached so no search is spent twice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs&lt;/strong&gt;: narration for my demo video, and an opt-in demo mode that reads the morning briefing and answers aloud.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hf26challenge</category>
      <category>devchallenge</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Farm Post: One Photo, and You're Selling</title>
      <dc:creator>Sohee Lee </dc:creator>
      <pubDate>Sat, 17 Oct 2026 21:58:40 +0000</pubDate>
      <link>https://dev.to/gracelee087/farm-post-one-photo-and-youre-selling-5fo1</link>
      <guid>https://dev.to/gracelee087/farm-post-one-photo-and-youre-selling-5fo1</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;This started with my grandmother. She grows peanuts and corn and sells them at the local market. But big supermarket chains have moved into rural areas too, and there is less and less room for sellers like her. She has never thought about selling online, because she doesn't know how to use the devices and apps it takes.&lt;/p&gt;

&lt;p&gt;That's where the idea came from: something that lets her, and the people in her rural area, buy and sell easily, with as few steps as possible. Taking a photo and tapping a few buttons should be enough. Farm Post is built for people like her: people who know their harvest, not marketing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Farm Post&lt;/strong&gt; is a Telegram bot for small farms that already sell directly to their customers: a farm shop, a market stall, a family with a field of persimmons.&lt;/p&gt;

&lt;p&gt;The farmer takes &lt;strong&gt;one photo&lt;/strong&gt; and sends it to the bot. That's the only thing they have to do.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4 reads the photo&lt;/strong&gt; and writes the whole post: a title, a short warm description and hashtags.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live online prices&lt;/strong&gt; for similar products appear next to the question "How much?", so the farmer has a reference. They tap a price, a unit (per kg, per piece, per jar…) and how many they have.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four ready-to-post designs&lt;/strong&gt; are drawn from the farmer's &lt;em&gt;real&lt;/em&gt; photo (banner, chalkboard, polaroid, magazine). No AI-generated images: what customers see is what they pick up.&lt;/li&gt;
&lt;li&gt;One more tap and the post is &lt;strong&gt;live in the farm's Telegram channel&lt;/strong&gt;, with the date, a listing number and a &lt;strong&gt;🛒 Reserve&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;A customer taps Reserve and chooses how many. &lt;strong&gt;Stock goes down by itself&lt;/strong&gt;, the post updates, and the farmer gets a message. They pay at pickup.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No website, no shop builder, no agency.&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%2F0lz29mvfiplc9qh3uvaj.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%2F0lz29mvfiplc9qh3uvaj.jpg" alt="The farmer's side in Telegram: Gemma's draft with online prices, tap price/unit/stock, the post in the channel, a reservation and the farmer's alert" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Who would pay for it?
&lt;/h3&gt;

&lt;p&gt;I didn't want to guess, so I looked for research on farms that sell direct. A 2025 survey of &lt;strong&gt;208 agricultural direct marketers in Germany&lt;/strong&gt; found:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"a considerable proportion of farmers are still reluctant to actively use these channels. The most frequently cited barriers are lack of time, and technical expertise, as well as uncertainties regarding the creation and planning of content."&lt;br&gt;
— &lt;a href="https://agrifoodecon.springeropen.com/articles/10.1186/s40100-025-00367-5" rel="noopener noreferrer"&gt;&lt;em&gt;When will I be successful? The use of social media in agricultural direct marketing in Germany&lt;/em&gt;, Agricultural and Food Economics 13, 23 (2025)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Farm Post is built around exactly those three barriers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Barrier from the study&lt;/th&gt;
&lt;th&gt;Farm Post&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No time&lt;/td&gt;
&lt;td&gt;One photo, then taps. No typing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No technical skills&lt;/td&gt;
&lt;td&gt;It's just Telegram. No website to build.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not sure what to post&lt;/td&gt;
&lt;td&gt;Gemma writes the text, and you pick one of four designs.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I'm building for two kinds of sellers. Some, like my grandmother, have never sold online and wouldn't know where to start. Others already have customers, but take orders by phone, one call at a time. Farm Post gives both a simple way to post what they have today and let customers reserve it. That's who I think would pay.&lt;/p&gt;

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

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

&lt;p&gt;&lt;a href="https://youtu.be/Ahk6svVCpEQ" rel="noopener noreferrer"&gt;https://youtu.be/Ahk6svVCpEQ&lt;/a&gt; (1:39, English narration and captions)&lt;/p&gt;

&lt;p&gt;Live demo channel: &lt;a href="https://t.me/demofarmshop_lee" rel="noopener noreferrer"&gt;https://t.me/demofarmshop_lee&lt;/a&gt; (open it in Telegram and tap 🛒 Reserve under a post to try the customer side)&lt;/p&gt;

&lt;p&gt;The video uses screenshots from a real run on 17 October 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gemma 4 (&lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt;) wrote the persimmon post in 38 seconds.&lt;/li&gt;
&lt;li&gt;The market line read: &lt;em&gt;Online shops (DE) sell "fresh persimmon fruit" for €1.19 to €118.28, typical €15.95 (23 offers)&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The post went to the channel with a Reserve button, and the reservation reached the farmer's chat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The four designs the bot made from that one persimmon photo:&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%2Foqh1okkcuesktoy1c383.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%2Foqh1okkcuesktoy1c383.jpg" alt="Four designs from the same real photo: banner, chalkboard, polaroid, magazine" width="800" height="800"&gt;&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/gracelee087" rel="noopener noreferrer"&gt;
        gracelee087
      &lt;/a&gt; / &lt;a href="https://github.com/gracelee087/farm-post" rel="noopener noreferrer"&gt;
        farm-post
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      One photo → a sales post in a farm's Telegram channel, with a Reserve button. Gemma 4 writes it, SerpApi shows market prices, runs on Render.
    &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;Farm Post&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;One photo → a finished sales post in the farm's Telegram channel, with a Reserve button
For small rural sellers (one person or a family): vegetables, fruit, eggs, honey, jam, hand-knitted goods.&lt;/p&gt;
&lt;p&gt;Built for the DEV Hacktoberfest Open-Source AI Challenge, Week 2 "Side Hustle".&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo video:&lt;/strong&gt; &lt;a href="https://youtu.be/Ahk6svVCpEQ" rel="nofollow noopener noreferrer"&gt;https://youtu.be/Ahk6svVCpEQ&lt;/a&gt; · &lt;strong&gt;Demo channel:&lt;/strong&gt; &lt;a href="https://t.me/demofarmshop_lee" rel="nofollow noopener noreferrer"&gt;https://t.me/demofarmshop_lee&lt;/a&gt;&lt;/p&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;The seller sends a photo to the Farm Post bot on Telegram.&lt;/li&gt;
&lt;li&gt;Gemma 4 reads the photo and writes the title, text, tags and a search query (&lt;code&gt;generate.py&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Online prices for that query (SerpApi, via &lt;code&gt;price-proxy/&lt;/code&gt;) are shown; the seller taps a price, unit and stock.&lt;/li&gt;
&lt;li&gt;Four designs are drawn from the real photo (&lt;code&gt;design.py&lt;/code&gt;); the seller picks one and taps Post.&lt;/li&gt;
&lt;li&gt;Customers tap 🛒 Reserve under the post, choose an amount, and pay at pickup. Stock goes down and the seller gets a message.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The writing rules live…&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/gracelee087/farm-post" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&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;&lt;code&gt;bot.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The Telegram bot: photo → price → unit → stock → design → post → reservations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;generate.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gemma 4 through the Gemini API, or local Ollama when no API key is set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;skills/farm-post/SKILL.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The writing rules Gemma follows, in the open &lt;a href="https://agentskills.io/specification" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt; format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;design.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Four designs from the real photo (Pillow)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;price-proxy/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A small server on Render that keeps the SerpApi key private&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Farmer's phone (Telegram)
  photo ──► bot.py
              ├─► Gemma 4 (open weights) reads the photo + SKILL.md rules
              │      → title, description, hashtags, a 2-4 word search query
              ├─► price-proxy on Render ─► SerpApi Google Shopping
              │      → lowest / typical / highest price, number of offers
              ├─► farmer taps price, unit, stock
              ├─► design.py → 4 designs from the real photo
              └─► Telegram channel post + 🛒 Reserve button
Customer taps Reserve → picks an amount → stock updates → farmer is notified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Gemma 4 is the core.&lt;/strong&gt; It's the only AI step, and it does the part farmers find hardest: looking at a photo and turning it into a post. I use Gemma 4 26B (&lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt;) through Google's Gemini API, and fall back to the 31B model if the API is busy. The prompt asks for strict JSON (item, title, body, tags, search query), so the bot can use every field directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rules live in a plain file anyone can edit.&lt;/strong&gt; What makes a good farm post is written down in &lt;code&gt;SKILL.md&lt;/code&gt;: the title is the product plus one thing you can actually see, the body is 3–5 friendly sentences ending with "Send me a message to reserve yours", and the model must &lt;strong&gt;never invent&lt;/strong&gt; prices, weights, origins or "organic". The bot reads those rules straight from the file. A farmer (or anyone) can change how posts are written without touching code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prices are never made up.&lt;/strong&gt; The farmer taps the price. Gemma only writes a search query, and SerpApi looks up what online shops charge. The bot always says it's "only a rough guide for a farm or market stall".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SerpApi + Render.&lt;/strong&gt; SerpApi gives the bot live Google Shopping results. The key must not sit in the bot, so a 185-line Python server (standard library only) on Render's free plan holds it. It caches each query for an hour and stops at 200 lookups a day so a free key can't be drained. The bot itself also runs on Render as a web service in webhook mode, defined in the same Blueprint (&lt;code&gt;price-proxy/render.yaml&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest limits&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing a post takes about &lt;strong&gt;30–40 seconds&lt;/strong&gt; on Google's free tier (measured: 30 s and 38 s for the persimmon, 23–54 s in other tests).&lt;/li&gt;
&lt;li&gt;Online-shop prices are noisy. In one persimmon search in Germany the results also included seeds and young trees, which is why ranges can be so wide.&lt;/li&gt;
&lt;li&gt;Render's free plan sleeps when idle, so the first price lookup after a break can be slow. The bot retries.&lt;/li&gt;
&lt;li&gt;The demo video was recorded while the bot ran on my laptop. It now runs on Render. Reservations have only been tested from my own account so far.&lt;/li&gt;
&lt;li&gt;Google's free tier for the Gemini API may use the data to improve Google's products. A farmer who doesn't want that can run Gemma locally instead (see below).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Because the farmer shouldn't be locked in.&lt;/p&gt;

&lt;p&gt;Gemma 4's weights are open (Apache 2.0). Today Farm Post calls Gemma through Google's free API because that's the cheapest way to start. But if that ever changes, gets too expensive, or a farmer doesn't want their photos on someone else's server, the &lt;strong&gt;same model&lt;/strong&gt; runs on their own computer: without an API key, &lt;code&gt;generate.py&lt;/code&gt; switches to Gemma 4 through Ollama. Nothing else changes. With a closed model, the business would depend on one company's price list and terms.&lt;/p&gt;

&lt;p&gt;Open weights also let me test models myself instead of trusting a model card. On my laptop (CPU only, no GPU), I ran the same three farm photos (potatoes, eggs, apples) through two open models, once each. Gemma 4 e4b took 71 seconds in total and Gemma 3 4B took 100 seconds. Both named all three products, but Gemma 3 added details that weren't in the photo, like "happy hens" and "German eggs". So the local fallback uses Gemma 4.&lt;/p&gt;

&lt;p&gt;The same goes for the rules. They're in an open Agent Skills file, not hidden in a prompt on a server. Farmers know their products better than I do, and they can fix the wording themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A customer-service agent.&lt;/strong&gt; It would answer simple questions from the listing data (name, price, stock) and pass everything else to the farmer. It's not built yet. It needs pickup times and an address first, so it never makes up an answer.&lt;/li&gt;
&lt;li&gt;Run the bot on Render full-time and test reservations with real customers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments.&lt;/strong&gt; Today customers pay at pickup. Later I'm thinking about connecting online payments and customer support, and turning Farm Post into a full product.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 4 (&lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt;) reads every photo and returns one strict JSON with five fields (item, title, body, tags, search query). The persimmon post took 38 seconds. Without an API key, the same flow runs on &lt;code&gt;gemma4:e4b&lt;/code&gt; through Ollama on a laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of SerpApi&lt;/strong&gt;: Google Shopping results sit next to the price question, so the farmer sees what real shops charge and the model never makes up a price. Gemma only writes a 2–4 word search query. For the persimmon: 23 offers, €1.19 to €118.28, typical €15.95.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render&lt;/strong&gt;: one Blueprint (&lt;code&gt;price-proxy/render.yaml&lt;/code&gt;) defines two web services: the Telegram bot in webhook mode, and a 185-line price server that keeps the SerpApi key private, caches each query for an hour and stops at 200 lookups a day.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hf26challenge</category>
      <category>devchallenge</category>
      <category>gemma</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Footnotes: your walk, as a beat</title>
      <dc:creator>Sohee Lee </dc:creator>
      <pubDate>Sun, 11 Oct 2026 17:51:56 +0000</pubDate>
      <link>https://dev.to/gracelee087/footnotes-your-walk-as-a-beat-2afm</link>
      <guid>https://dev.to/gracelee087/footnotes-your-walk-as-a-beat-2afm</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;p&gt;Most of us walk the same streets every day and stop hearing them. Footsteps, traffic, a door, a dog two houses down: it all becomes background. I wanted a reason to listen again, and a way to bring a walk home as something you would actually play twice.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Footnotes turns a walk into a beat.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;Record a walk&lt;/strong&gt; and go outside. The phone listens while you walk. When you come home, the real sounds you passed become a 30-second beat, on a rhythm measured from the walk itself. Then you hear that beat as three beatboxes side by side, while a pair of neon lips beatboxes along.&lt;/p&gt;

&lt;p&gt;Other apps tell you what you heard outside. Footnotes makes music out of it. And it only works if you go out: the screen is just where the walk comes back to you. A trip to the shop is enough; you do not need birds or a view.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Live app (Android Chrome):&lt;/strong&gt; &lt;a href="https://gracelee087.github.io/footnotes/" rel="noopener noreferrer"&gt;https://gracelee087.github.io/footnotes/&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;No walk at hand? The home screen has two short samples. Their sounds are AI-generated with ElevenLabs (labelled in the app), and their Gemma 4 beat and beatboxes are saved, so trying them costs nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Taking it outside
&lt;/h3&gt;

&lt;p&gt;I tested Footnotes for two days, on walks and on my way to school, with the phone in my pocket.&lt;/p&gt;

&lt;p&gt;Every step became fun. Knowing the walk would come back as a beat, I started listening on purpose, and every small sound felt worth keeping. My senses woke up on a route I thought I already knew.&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/gracelee087" rel="noopener noreferrer"&gt;
        gracelee087
      &lt;/a&gt; / &lt;a href="https://github.com/gracelee087/footnotes" rel="noopener noreferrer"&gt;
        footnotes
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Your walk, as a beat. YAMNet listens on the phone, Gemma 4 makes the beat, ElevenLabs beatboxes it, DigitalOcean runs the server.
    &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;Footnotes&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Your walk, as a beat.&lt;/strong&gt; Record the street, and Footnotes turns the real sounds you brought home into a 30-second beat, then into three beatboxes, while neon lips beatbox along.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live app (Android Chrome):&lt;/strong&gt; &lt;a href="https://gracelee087.github.io/footnotes/" rel="nofollow noopener noreferrer"&gt;https://gracelee087.github.io/footnotes/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo video:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=rgViikcZVBI" rel="nofollow noopener noreferrer"&gt;https://www.youtube.com/watch?v=rgViikcZVBI&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Built for the Hacktoberfest Open-Source AI Challenge, Week 1: Touch Grass (repository started 6 October 2026).&lt;/p&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;Record.&lt;/strong&gt; Tap &lt;strong&gt;Record a walk&lt;/strong&gt; and go outside. The open model &lt;strong&gt;YAMNet&lt;/strong&gt; listens on the phone and keeps one real snippet of each sound it hears (footsteps, cars, water, dogs…). The recording never leaves the phone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compress.&lt;/strong&gt; The app measures the walk's own rhythm (tempo, loud and soft hits on a 16-step bar). &lt;strong&gt;Gemma 4&lt;/strong&gt; names the walk and makes the beat: which real snippets play it and the rhythm, with a one-line reason on the beat card. Recorded speech is never used as a beat sound.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beatbox.&lt;/strong&gt; The same beat as…&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/gracelee087/footnotes" 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;p&gt;Three steps, all automatic: &lt;strong&gt;Record → Compress → Beatbox&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Listen, on the phone.&lt;/strong&gt; The browser records the microphone with voice processing turned off, so the street sounds like the street. At the same time the open model &lt;strong&gt;YAMNet&lt;/strong&gt;, running in the browser through MediaPipe, recognises the sound every half second. I mapped its classes into 46 everyday sound types (footsteps, doors, dogs, water, bike bells…), and for each type the app keeps one real snippet. The recording never leaves the phone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Compress, with Gemma 4.&lt;/strong&gt; The app finds where the walk hit hard or soft and folds those hits onto one 16-step bar: the hardest become kicks, then snares, then hi-hats. &lt;strong&gt;Gemma 4&lt;/strong&gt; names the walk and makes the beat: it picks which real snippets play it, and the beat card shows why, for example &lt;em&gt;"Gemma: Steady steps provide a pulse while splashing water offers a calm contrast."&lt;/em&gt; Speech is never used as a beat sound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Beatbox, three ways.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Human mouth:&lt;/strong&gt; every hit replayed by a real recorded mouth sound. No generative AI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElevenLabs Clean&lt;/strong&gt; and &lt;strong&gt;ElevenLabs Deep:&lt;/strong&gt; ElevenLabs Music, given only numbers: tempo, length and which steps carry the kick, snare and hi-hat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Surprise mix:&lt;/strong&gt; the mouth takes cut and remixed on the beat grid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each card shows a &lt;strong&gt;Rhythm match&lt;/strong&gt; and a &lt;strong&gt;Loud/soft match&lt;/strong&gt;, so you can hear and see how closely each beatbox follows your walk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lips are real.&lt;/strong&gt; I traced a human beatboxer frame by frame with MediaPipe Face Landmarker. Only the points ship, no video. On each hit the lips replay a real kick, snare or hi-hat.&lt;/p&gt;

&lt;h3&gt;
  
  
  The partners
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4&lt;/strong&gt; (&lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt;, via the Gemini API): names every walk (a short title and one line) and makes its beat: it picks 2 to 4 of the real snippets, a rhythm style for walks without a clear rhythm, and writes the one-line reason on the beat card. It sees only sound names and counts, never audio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElevenLabs:&lt;/strong&gt; three features. &lt;strong&gt;Music API&lt;/strong&gt; makes the two AI beatboxes from the walk's numbers only: &lt;em&gt;Clean&lt;/em&gt; with a composition plan, &lt;em&gt;Deep&lt;/em&gt; with a prompt. &lt;strong&gt;Sound Effects&lt;/strong&gt; made the sounds of the two sample walks. &lt;strong&gt;Text to Speech&lt;/strong&gt; narrates the demo video.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DigitalOcean App Platform:&lt;/strong&gt; one small Node server, deployed from the GitHub repo. It holds the Gemini and ElevenLabs keys so the phone never does, accepts requests only from the app's page, runs every Gemma 4 and ElevenLabs call, and keeps a shared daily cap (the home screen shows how many walks are left today).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Open source used
&lt;/h3&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;Role&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;YAMNet&lt;/td&gt;
&lt;td&gt;Recognises sounds on the phone&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MediaPipe Tasks Audio&lt;/td&gt;
&lt;td&gt;Runs YAMNet in the browser&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MediaPipe Face Landmarker&lt;/td&gt;
&lt;td&gt;Traced the lip motion (offline, once)&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4&lt;/td&gt;
&lt;td&gt;Names the walk and makes the beat&lt;/td&gt;
&lt;td&gt;Gemma license (open weights)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mouth sounds by giddster and ipaghost (Freesound)&lt;/td&gt;
&lt;td&gt;The human-mouth beatbox&lt;/td&gt;
&lt;td&gt;CC0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spencer X, &lt;em&gt;One Beatboxer, 20 Beatbox Snares&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Source of the lip motion&lt;/td&gt;
&lt;td&gt;CC BY 3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Footnotes&lt;/td&gt;
&lt;td&gt;Everything else&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;&lt;strong&gt;Because the recording stays in your pocket.&lt;/strong&gt; A walk recording is private: it catches voices, your street, your routine. An open model small enough to run in the browser means the listening happens on the phone, and only numbers leave it. A closed audio API would need the whole recording just to tell you a dog barked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Because it is checkable.&lt;/strong&gt; The rhythm, the snippets and the match scores are plain code anyone can read and change. The open core (YAMNet, the beat, the human-mouth beatbox, the lips) works without an account.&lt;/p&gt;

&lt;p&gt;To be honest about the rest: ElevenLabs is a paid closed service, and Gemma runs through Google's API rather than on the phone.&lt;/p&gt;

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

&lt;p&gt;I built Footnotes in one week with Claude Code as my coding agent. The repo is the record: 42 commits between Oct 5 and Oct 11, 36 of them co-authored with Claude.&lt;/p&gt;

&lt;p&gt;The commit history shows how the idea changed along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Day 1:&lt;/strong&gt; a walk became a written score, with each kind of sound mapped to an instrument, then a short beat made from the real snippets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 2:&lt;/strong&gt; beatboxes arrived to perform the beat, and the neon lips were traced from a real beatboxer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 3:&lt;/strong&gt; Gemma 4 started naming each walk, and the server moved to DigitalOcean.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Final days:&lt;/strong&gt; Gemma 4 began picking the beat's sounds and rhythm, and I removed a remix feature ("Drop it, Gemma!") that the app no longer used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I brought the idea and tested it on real walks; Claude Code and I wrote the code together.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; Gemma 4 names every walk and makes its beat, with a one-line reason on the beat card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of DigitalOcean:&lt;/strong&gt; the server on App Platform holds the keys and runs every Gemma 4 and ElevenLabs call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs:&lt;/strong&gt; two AI beatboxes, the sample sounds and the demo narration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading. If you try it, take it somewhere ordinary: a bus stop, a market, your own street. Then see what beat comes back.&lt;/p&gt;

</description>
      <category>hf26challenge</category>
      <category>devchallenge</category>
    </item>
    <item>
      <title>Which AI lies less in marketing copy?</title>
      <dc:creator>Sohee Lee </dc:creator>
      <pubDate>Tue, 06 Oct 2026 19:52:06 +0000</pubDate>
      <link>https://dev.to/gracelee087/which-ai-lies-less-in-marketing-copy-4lah</link>
      <guid>https://dev.to/gracelee087/which-ai-lies-less-in-marketing-copy-4lah</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/kaggle-2026-09-23"&gt;Kaggle Benchmarking Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'm exploring a venture I could run as a solo founder, with multiple AI agents working together as my staff.&lt;/p&gt;

&lt;p&gt;I've been using AI to write company introductions and marketing copy, and it has been frustrating because it keeps making things up. What I wanted was help explaining the venture; what I ended up needing to know was which agent is least likely to invent facts.&lt;/p&gt;

&lt;p&gt;So I turned that frustration into a benchmark: &lt;strong&gt;11 models, 20 fictional companies, and four different writing instructions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One response captures the problem. I asked Claude Sonnet 4.5 to write a LinkedIn post for &lt;strong&gt;Coupon Lens&lt;/strong&gt;, a fictional bond-research tool. I supplied a company brief and told it to use only the facts provided. The brief contained no founder biography.&lt;/p&gt;

&lt;p&gt;Claude wrote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After years of working in fixed income markets, I saw how fragmented the research process had become.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;I had described a product. The model added the founder's career history and a personal reason for building it.&lt;/strong&gt; Neither was in the brief.&lt;/p&gt;

&lt;p&gt;My separate AI checker, glm-5, flagged the career claim. This happened even with the facts-only instruction in place.&lt;/p&gt;

&lt;p&gt;Across the benchmark, that instruction helped: the share of texts passing the checker rose from &lt;strong&gt;19% to 68%&lt;/strong&gt;. It still left errors like this one. Reading the outputs also revealed more reuse of the company briefs, which led me to test a possible fix on ten new companies.&lt;/p&gt;

&lt;p&gt;The experiment diary records the predictions, quota problems and corrections along the way.&lt;/p&gt;

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

&lt;p&gt;I wanted to know whether a model could turn a company description into persuasive copy while keeping its claims supported by that description, and which instructions helped it do so.&lt;/p&gt;

&lt;p&gt;I made a dataset of &lt;strong&gt;20 fictional early-stage companies&lt;/strong&gt;: ten solo founders and ten small teams. Each brief had seven fields, including the product, audience, team and price. I asked each model for three kinds of copy: a homepage headline and subheadline, a LinkedIn post, and a cold email to a potential customer.&lt;/p&gt;

&lt;p&gt;The facts stayed the same. The instruction changed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;What I added&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A: no extra rule&lt;/td&gt;
&lt;td&gt;Just the brief and the writing request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B: don't invent numbers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Do not invent numbers.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C: facts only&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Use only the facts above. Do not add numbers, clients, team members, awards, or claims that are not listed.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D: repeat the brief&lt;/td&gt;
&lt;td&gt;The same facts-only rule, plus the fact sheet repeated immediately before the request&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A separate AI, &lt;strong&gt;&lt;code&gt;glm-5&lt;/code&gt;&lt;/strong&gt;, compared each piece of copy with its company brief. It flagged claims it judged to go beyond the supplied information. &lt;strong&gt;A pass means this checker found no unsupported claims.&lt;/strong&gt; The analysis calls a pass &lt;strong&gt;“reader-clean.”&lt;/strong&gt; No person graded the outputs, and the score does not measure writing quality or deliberate deception.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  The full company brief behind the opening LinkedIn post
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Company: Coupon Lens
What it does: A research tool for bond investors. The software shows which bonds look expensive or cheap and explains why, combining fixed income research, quantitative models and scenario analysis. Covers interest rates and mortgage-backed securities; municipal and corporate bonds are planned.
Who it is for: Portfolio managers, traders, analysts and investment advisers
Location: Charlotte, North Carolina
Founded: 2025
Team: One founder. No employees.
Price: $1,500 per seat per month.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The checker looks for unsupported numbers, team claims, clients, awards, superlatives and features. A rule-based detector provides a secondary score.&lt;/p&gt;



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

&lt;p&gt;The &lt;strong&gt;11 writing models&lt;/strong&gt; came from &lt;strong&gt;Google, OpenAI and Anthropic&lt;/strong&gt; and ran through Kaggle's Model Proxy. I included selected smaller and larger models from the same provider to compare their results. The separate checker, glm-5, assessed their copy.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  The 11 models and how much data each run produced
  &lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Models&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;gemini-2.5-flash, gemini-3.1-flash-lite, gemini-3.7-flash, gemini-3.8-flash, gemma-4-31b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;td&gt;gpt-5.4, gpt-5.4-nano, gpt-oss-120b, gpt-oss-20b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anthropic&lt;/td&gt;
&lt;td&gt;claude-sonnet-4-5, claude-sonnet-5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table uses shortened model names; the &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/results/manifest.json" rel="noopener noreferrer"&gt;response manifest&lt;/a&gt; records the full saved model slugs and run IDs. Models used their provider defaults, with explicit output caps for Claude; compute was not equalized.&lt;/p&gt;

&lt;p&gt;Each complete run planned 240 texts, plus 48 extra responses to check repeatability. The selected runs produced &lt;strong&gt;2,898 texts&lt;/strong&gt;, of which &lt;strong&gt;2,665&lt;/strong&gt; received a reader score. The main analysis uses &lt;strong&gt;2,208 scored first-repeat texts&lt;/strong&gt;: one response per model, company, writing format and instruction, excluding the extra repeats. Failed or unscored outputs are excluded, never counted as passes.&lt;/p&gt;



&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

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

&lt;h3&gt;
  
  
  1. A number ban reduced other flagged claims too
&lt;/h3&gt;

&lt;p&gt;Before the main runs, I expected &lt;code&gt;Do not invent numbers.&lt;/code&gt; to reduce number claims while leaving other unsupported claims unchanged or making them more frequent. A model might stop adding customer counts, for example, but still add unlisted features or boasts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pooled results contradicted that prediction.&lt;/strong&gt; When I compared outputs from the same model, company and writing format, the checker flagged fewer numeric claims and fewer other claims too.&lt;/p&gt;

&lt;p&gt;The pass rate went from &lt;strong&gt;19.0% with no extra rule&lt;/strong&gt; to &lt;strong&gt;37.1% with the number rule&lt;/strong&gt;. Telling the model to use only the supplied facts raised it to &lt;strong&gt;67.7%&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%2Fraw.githubusercontent.com%2Fgracelee087%2Fai-marketing-honesty-bench%2Fc65372066e6cf9357e0867a2b69b0667dc8f1e87%2Fresults%2Fanalysis%2Fhypothesis_views%2F01_conditions_en.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%2Fraw.githubusercontent.com%2Fgracelee087%2Fai-marketing-honesty-bench%2Fc65372066e6cf9357e0867a2b69b0667dc8f1e87%2Fresults%2Fanalysis%2Fhypothesis_views%2F01_conditions_en.png" alt="AI checker pass rates: no extra rule 19.0%, do not invent numbers 37.1%, facts only 67.7%, and facts plus a repeated brief 70.7%. Counts appear above each bar."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeating the brief raised the observed pass rate a little further, to &lt;strong&gt;70.7%&lt;/strong&gt;. To test its added benefit, my analysis plan compared the &lt;strong&gt;average number of flagged claims per text&lt;/strong&gt; on matching items. That comparison was too uncertain to establish an extra benefit from repetition.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Exact counts and how I checked the differences
  &lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prompt&lt;/th&gt;
&lt;th&gt;Texts passing the checker&lt;/th&gt;
&lt;th&gt;Mean flagged claims per text&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A: no extra rule&lt;/td&gt;
&lt;td&gt;105 / 552 (19.0%)&lt;/td&gt;
&lt;td&gt;2.34&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B: don't invent numbers&lt;/td&gt;
&lt;td&gt;205 / 552 (37.1%)&lt;/td&gt;
&lt;td&gt;1.54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C: facts only&lt;/td&gt;
&lt;td&gt;371 / 548 (67.7%)&lt;/td&gt;
&lt;td&gt;0.74&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D: facts repeated&lt;/td&gt;
&lt;td&gt;393 / 556 (70.7%)&lt;/td&gt;
&lt;td&gt;0.67&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The bars summarize all available scored texts. The statistical comparisons use matching items: the same model, company and writing format under two instructions. I estimate uncertainty by resampling companies 10,000 times, keeping each company's texts together. An interval that includes zero leaves no change as a plausible result.&lt;/p&gt;

&lt;p&gt;B minus A reduced numeric claims by &lt;strong&gt;0.20 per text&lt;/strong&gt; (95% interval −0.26 to −0.13) and other claims by &lt;strong&gt;0.59&lt;/strong&gt; (−0.80 to −0.39). C minus B reduced total claims by about &lt;strong&gt;0.80&lt;/strong&gt; (−0.96 to −0.65). D minus C was &lt;strong&gt;−0.08&lt;/strong&gt; (−0.23 to +0.06): its interval included no change.&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%2Fraw.githubusercontent.com%2Fgracelee087%2Fai-marketing-honesty-bench%2Fc65372066e6cf9357e0867a2b69b0667dc8f1e87%2Fresults%2Fanalysis%2Fhypothesis_views%2F02_hypotheses_en.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%2Fraw.githubusercontent.com%2Fgracelee087%2Fai-marketing-honesty-bench%2Fc65372066e6cf9357e0867a2b69b0667dc8f1e87%2Fresults%2Fanalysis%2Fhypothesis_views%2F02_hypotheses_en.png" alt="Paired changes with 95% confidence intervals: the number rule reduced numeric and other claims; facts-only instructions reduced claims further; repeating the brief had an uncertain extra effect."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/HYPOTHESES.md" rel="noopener noreferrer"&gt;original hypotheses and dated reporting changes&lt;/a&gt; preserve the prediction that failed.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The model added a founder's backstory
&lt;/h3&gt;

&lt;p&gt;The Coupon Lens brief described the product, its audience and its price. It said the company had one founder. It gave no details about that person's previous work or motivation for starting the company.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Information supplied about the founder&lt;/th&gt;
&lt;th&gt;Wording added by Sonnet 4.5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No employment history&lt;/td&gt;
&lt;td&gt;After years of working in fixed income markets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No personal account of the problem&lt;/td&gt;
&lt;td&gt;I saw how fragmented the research process had become.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That sentence makes the post read like a founder speaking from professional experience. A reader could reasonably take it as a claim about the person behind the product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The checker flagged the career claim.&lt;/strong&gt; The example shows an unsupported addition by the writing model under the facts-only instruction.&lt;/p&gt;

&lt;p&gt;I selected this post &lt;strong&gt;after inspecting the saved outputs&lt;/strong&gt;. It illustrates one failure; I did not run a separate study measuring how often models invent founder biographies. The &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/results/selected_runs/4/claude-sonnet-4-5-20250929/3993424/results.jsonl#L199" rel="noopener noreferrer"&gt;saved response and checker judgment&lt;/a&gt; preserve the full post.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Models included “no employees” more often
&lt;/h3&gt;

&lt;p&gt;A different example showed what the checker did not measure. I gave Gemini 3.8 Flash a brief for Rackhand Robotics, a fictional company developing robots for data centers. Its team field said &lt;code&gt;One founder. No employees.&lt;/code&gt; With the facts-only instruction, Gemini included this in its sales email:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I run the company as a single founder with no employees.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The statement came directly from the brief, and the email passed the checker. The score did not tell me whether mentioning the employee count helped introduce the robots to a potential customer. I also counted how often that detail appeared across the study.&lt;/p&gt;

&lt;p&gt;All ten solo-founder briefs already said “No employees.” Without an extra rule, that detail appeared in &lt;strong&gt;0.7%&lt;/strong&gt; of the generated texts about those companies. With the facts-only instruction, it appeared in &lt;strong&gt;21.0%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In these cases, the models repeated a supplied fact. The result describes how often they included that fact in the copy.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;gemini-3.8-flash&lt;/code&gt;, all &lt;strong&gt;60&lt;/strong&gt; outputs under the facts-only instruction passed the checker: one response for each of the 20 companies and three writing formats. I separately counted shared wording, repeated phrases and the employee detail in those same texts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What appeared in the text&lt;/th&gt;
&lt;th&gt;Texts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;At least half the words matched phrases in the brief&lt;/td&gt;
&lt;td&gt;29 / 60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;At least 5% of four-word sequences repeated within the text&lt;/td&gt;
&lt;td&gt;23 / 60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;“No employees”&lt;/td&gt;
&lt;td&gt;19 / 60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;None of the three above&lt;/td&gt;
&lt;td&gt;12 / 60&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One text can appear in several rows. I chose these checks &lt;strong&gt;after seeing the outputs&lt;/strong&gt; to describe what I had noticed. They do not establish a quality ranking. Reusing technical wording can be useful, and some founders may want to emphasize their size. Nobody rated how persuasive these texts were.&lt;/p&gt;

&lt;p&gt;All 60 texts passed the factuality check. That pass alone tells us nothing about whether readers would find the wording relevant, repetitive or persuasive.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  The two email endings, staffing counts and text checks
  &lt;p&gt;The full Rackhand Robotics brief was:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Company: Rackhand Robotics
What it does: Developing robots that do routine data center work: installing and swapping hardware and running maintenance checks. The robots are in development and not yet shipping.
Who it is for: Data center operators
Location: Denver, Colorado
Founded: 2025
Team: One founder. No employees.
Price: Not yet announced.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For this company, Gemini wrote this with no extra rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Would you be open to a brief, 15-minute call next week to share what physical tasks consume most of your team's time?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the facts-only instruction, the body ended:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As development continues, I wanted to introduce Rackhand Robotics and our upcoming robots to data center operators. Thank you for your time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first email also described “autonomous robots” and “verifying connections,” which the checker flagged as unsupported. The second had no flags. This example was selected after inspecting the results; it does not establish how often emails lost an invitation.&lt;/p&gt;

&lt;p&gt;Across all three writing formats, these solo-company texts contained “no employees” or a listed spelling variant:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prompt&lt;/th&gt;
&lt;th&gt;Generated texts about solo-founder companies&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A: no extra rule&lt;/td&gt;
&lt;td&gt;2 / 304 (0.7%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B: don't invent numbers&lt;/td&gt;
&lt;td&gt;16 / 300 (5.3%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C: facts only&lt;/td&gt;
&lt;td&gt;63 / 300 (21.0%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D: facts repeated&lt;/td&gt;
&lt;td&gt;59 / 303 (19.5%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These counts cover generated texts about solo-founder companies, including texts the checker could not score. That is why their totals differ from the pass-rate table, which includes both solo and team companies but only scored texts.&lt;/p&gt;

&lt;p&gt;For word overlap, a word counts when it belongs to an exact matching sequence of four words, ignoring case and punctuation. Product names and legitimate technical descriptions count too. The repetition check measures repeated four-word sequences within a text.&lt;/p&gt;



&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. I tested a one-sentence fix on new companies
&lt;/h3&gt;

&lt;p&gt;Could I reduce the reuse of the brief by explicitly saying the models could leave some facts out? I added this sentence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Select the facts relevant to this audience; you do not need to include every field.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added it to the facts-only instruction and called that version &lt;strong&gt;E&lt;/strong&gt;. Then I tested A, C and E on &lt;strong&gt;ten new companies&lt;/strong&gt;, using GPT-5.4, GPT-5.4 nano, Gemini 3.8 Flash and Sonnet 5.&lt;/p&gt;

&lt;p&gt;All &lt;strong&gt;120 emails&lt;/strong&gt; were generated. The checker scored &lt;strong&gt;109&lt;/strong&gt;; eleven judgments hit its output limit and were left out of the checker results. I measured wording shared with the brief in all 120 emails, including those without a checker score.&lt;/p&gt;

&lt;p&gt;For each email, I counted the percentage of words covered by exact four-word matches with its brief. The graph shows the average for each instruction. I call this &lt;strong&gt;word overlap&lt;/strong&gt;; legitimate product descriptions count too.&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%2Fraw.githubusercontent.com%2Fgracelee087%2Fai-marketing-honesty-bench%2Fc65372066e6cf9357e0867a2b69b0667dc8f1e87%2Fresults%2Fanalysis%2Fhypothesis_views%2F03_followup_en.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%2Fraw.githubusercontent.com%2Fgracelee087%2Fai-marketing-honesty-bench%2Fc65372066e6cf9357e0867a2b69b0667dc8f1e87%2Fresults%2Fanalysis%2Fhypothesis_views%2F03_followup_en.png" alt="On ten new companies, mean word overlap was 7.9% with no extra rule, 22.4% with facts only and 21.9% with the extra sentence. The original increase repeated; the proposed reduction remained uncertain."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The original pattern appeared again.&lt;/strong&gt; Average word overlap rose from &lt;strong&gt;7.9%&lt;/strong&gt; to &lt;strong&gt;22.4%&lt;/strong&gt; with the facts-only rule. It increased in all four writing models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The extra sentence did not clearly reduce overlap.&lt;/strong&gt; The average was &lt;strong&gt;21.9%&lt;/strong&gt; with permission to select relevant facts. The difference from 22.4% was too uncertain to establish an improvement.&lt;/p&gt;

&lt;p&gt;For Gemini 3.8 Flash, both the facts-only rule and the rule with the extra sentence produced “no employees” in all five solo-company emails. All ten of its emails under each of those two instructions also passed the checker.&lt;/p&gt;

&lt;p&gt;The added instruction sounded promising, but this experiment did not give me evidence to recommend it as a way to reduce copying.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Follow-up design, uncertainty and checker results
  &lt;p&gt;Before these calls, I locally froze the briefs, prompts, predictions, reader and analysis with file hashes. This was not independently timestamped. Every new brief included a capability limitation, making this a targeted stress test. Emails had a 100–150-word target, which may encourage filling space. No writer reached its output cap.&lt;/p&gt;

&lt;p&gt;The paired C-minus-A increase in overlap was &lt;strong&gt;14.54 percentage points&lt;/strong&gt; (95% company-bootstrap interval &lt;strong&gt;12.55 to 16.38&lt;/strong&gt;, 40 pairs). E minus C was &lt;strong&gt;−0.55 percentage points&lt;/strong&gt; (interval &lt;strong&gt;−1.97 to +0.89&lt;/strong&gt;, 40 pairs).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Checker passes&lt;/th&gt;
&lt;th&gt;Solo-company emails saying “no employees”&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A: no extra rule&lt;/td&gt;
&lt;td&gt;6/35&lt;/td&gt;
&lt;td&gt;0/20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C: facts only&lt;/td&gt;
&lt;td&gt;25/37&lt;/td&gt;
&lt;td&gt;6/20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E: facts only + select relevant facts&lt;/td&gt;
&lt;td&gt;26/37&lt;/td&gt;
&lt;td&gt;5/20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The A-to-C checker comparison is descriptive: two writers had fewer than the eight paired judgments required by the frozen protocol. For E versus C, the change on the &lt;strong&gt;35 matched scored pairs was zero&lt;/strong&gt; (interval −13.9 to +13.5 percentage points). That does not establish equal factuality.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/tree/master/validation_2026-10-05" rel="noopener noreferrer"&gt;protocol, complete results and saved responses&lt;/a&gt; document the separate follow-up, including execution repairs and the reader's missing responses.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Which model scored highest?
&lt;/h3&gt;

&lt;p&gt;For the main model comparison, I used the &lt;strong&gt;first 12 companies&lt;/strong&gt; because later companies suffered more quota failures. The scores combine all four original instructions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GPT-5.4 had the highest observed checker pass rate: 78.2%.&lt;/strong&gt; Gemini 3.8 Flash followed at &lt;strong&gt;72.9%&lt;/strong&gt;, and GPT-5.4 nano scored &lt;strong&gt;52.1%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some models had fewer scored outputs than others. When I restricted a further comparison to the same items for ten models with enough data, GPT-5.4 and Gemini 3.8 Flash tied. So the available results do not establish a clear winner. They also do not tell us which model writes the most useful marketing.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  All 11 models, coverage and the model-size comparison
  &lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Reader-clean rate&lt;/th&gt;
&lt;th&gt;Scored texts, out of 144&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gpt-5.4&lt;/td&gt;
&lt;td&gt;78.2%&lt;/td&gt;
&lt;td&gt;142&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemini-3.8-flash&lt;/td&gt;
&lt;td&gt;72.9%&lt;/td&gt;
&lt;td&gt;144&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemini-3.7-flash&lt;/td&gt;
&lt;td&gt;67.7%&lt;/td&gt;
&lt;td&gt;130&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemma-4-31b&lt;/td&gt;
&lt;td&gt;62.5%&lt;/td&gt;
&lt;td&gt;144&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gpt-5.4-nano&lt;/td&gt;
&lt;td&gt;52.1%&lt;/td&gt;
&lt;td&gt;144&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;claude-sonnet-5&lt;/td&gt;
&lt;td&gt;47.9%&lt;/td&gt;
&lt;td&gt;142&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;claude-sonnet-4-5&lt;/td&gt;
&lt;td&gt;34.8%&lt;/td&gt;
&lt;td&gt;135&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemini-3.1-flash-lite&lt;/td&gt;
&lt;td&gt;33.8%&lt;/td&gt;
&lt;td&gt;136&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemini-2.5-flash&lt;/td&gt;
&lt;td&gt;29.2%&lt;/td&gt;
&lt;td&gt;144&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gpt-oss-120b&lt;/td&gt;
&lt;td&gt;18.9%&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gpt-oss-20b&lt;/td&gt;
&lt;td&gt;15.7%&lt;/td&gt;
&lt;td&gt;140&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The full analysis includes intervals and a comparison restricted to 110 identical items shared by the ten models with at least 100 scored items. On those shared items, GPT-5.4 and Gemini 3.8 Flash tie at &lt;strong&gt;75.5%&lt;/strong&gt;. Comparing the two directly on their 142 shared scored items gives GPT-5.4 a 4.9-percentage-point lead, with an interval from −4.2 to +13.9 points. This does not establish a clear winner. These are additional sensitivity checks, not a replacement ranking. The gpt-oss-120b estimate is particularly sparse.&lt;/p&gt;

&lt;p&gt;Neither OpenAI smaller-versus-larger pair established a difference in flagged-claim counts. The Gemini pair favored 3.8 Flash over 3.1 Flash Lite, but also changed model generation. That leaves the effect of size alone unresolved.&lt;/p&gt;

&lt;p&gt;The Kaggle task score uses all available scored first-repeat texts across 20 companies, so it can differ from this 12-company table. When multiple runs exist, I report the one with the most reader-scored items; ties go to the later run. Runs are never merged. These reporting rules were disclosed after some results were available. The &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/results/run_selection.csv" rel="noopener noreferrer"&gt;run-selection audit&lt;/a&gt; records coverage and selection.&lt;/p&gt;

&lt;p&gt;One live score also comes from a different run: on October 6, Kaggle showed &lt;strong&gt;18.18%&lt;/strong&gt; for gpt-oss-120b from run 4046443. The higher-coverage run selected here, 3866186, scored &lt;strong&gt;20.0%&lt;/strong&gt; across all 20 companies and &lt;strong&gt;18.9%&lt;/strong&gt; in the 12-company table above. The other ten live scores matched the corresponding all-company saved-run scores.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  My experiment diary &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I kept the predictions and later changes in dated files. Here is a condensed diary reconstructed from those records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;September 28 — Write down what could prove me wrong.&lt;/strong&gt; Before the main runs, I recorded five directional hypotheses. One was that banning invented numbers would push unsupported claims into other categories. I kept that prediction in the record when the results contradicted it. &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/HYPOTHESES.md" rel="noopener noreferrer"&gt;Original hypotheses&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;September 30 — The quota changed the comparison.&lt;/strong&gt; Later companies were losing more responses to daily limits. I moved the primary model comparison to the first twelve companies and documented the change after four models had run. Prompt comparisons still use all twenty. &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/HYPOTHESES.md#addendum-2--cross-model-comparisons-use-pairs-16-2026-09-30-after-4-models-before-the-remaining-models" rel="noopener noreferrer"&gt;Dated analysis change&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;October 4–5 — Decide which rerun counts.&lt;/strong&gt; Some runs were mostly missing. I recorded a coverage-based selection rule: use one run per model, choosing the most reader-scored outputs, with the later run breaking ties. The hypotheses file records when each rule was added. &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/results/run_selection.csv" rel="noopener noreferrer"&gt;Run-selection record&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;October 5 — My own measurement needed fixing.&lt;/strong&gt; An automatic check missed real invitations in the emails. I corrected its rules and removed invitation rates from the main findings. A measurement bug can create a convincing story too. &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/analysis/METHOD_CHANGES.md#invitation-rules" rel="noopener noreferrer"&gt;Correction log&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;October 5 — Give the proposed fix a fresh test.&lt;/strong&gt; I locally froze the follow-up plan before the new calls, then generated 120 emails using ten new companies. The increase in copied wording repeated; the extra selection sentence did not clearly reduce it. Both outcomes stay in the report. &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/tree/master/validation_2026-10-05" rel="noopener noreferrer"&gt;Follow-up protocol and results&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  The original predictions, in plain English
  &lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prediction&lt;/th&gt;
&lt;th&gt;What the results say&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;H1: Without a rule, models add unsupported claims, including teams for solo founders.&lt;/td&gt;
&lt;td&gt;Supported in these outputs: the reader flagged claims in 81% of baseline texts; team claims appeared in 4.7% of scored solo-founder baseline texts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H2: Banning invented numbers shifts unsupported claims into other categories.&lt;/td&gt;
&lt;td&gt;Contradicted in the pooled comparison: numbers and other claims both fell.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H3: A facts-only rule reduces claims more than a number ban.&lt;/td&gt;
&lt;td&gt;Supported in the paired comparison.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H4: Repeating the brief helps further.&lt;/td&gt;
&lt;td&gt;Unclear: the interval included no change.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H5: Smaller models add more claims than larger models from the same provider.&lt;/td&gt;
&lt;td&gt;Unresolved as a general size effect: both OpenAI pairs were inconclusive; the Gemini pair also changed model generation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These summarize the five directional hypotheses. The original record also includes measurements without directional predictions: generation, cost and repeat stability. Their outputs remain in the &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/results/analysis/summary.md" rel="noopener noreferrer"&gt;full analysis&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What I would actually use
&lt;/h3&gt;

&lt;p&gt;For these tasks, &lt;strong&gt;“use only the facts above” reduced the claims flagged by the checker&lt;/strong&gt;. Repeating the brief had no clear extra benefit. Adding permission to select relevant facts did not clearly reduce copying in the follow-up.&lt;/p&gt;

&lt;p&gt;I would check claims about the people behind a company as carefully as its product features and price. A founder's past work and personal reasons for building a product need a source too. The opening post is a reminder to check those sentences even when they fit naturally into the story.&lt;/p&gt;

&lt;p&gt;I would review a draft in two steps. First, compare its claims with the source information. Then, read it as the intended customer: does each detail help explain why the product matters to me? The “no employees” sentence passed the first check. This study has no customer ratings to answer the second.&lt;/p&gt;

&lt;p&gt;There is also a reason to be cautious about the first review. The checker is another AI. For example, it rejected “autonomous robots,” although a person might infer autonomy from the product description. Its flags are judgments, not proof of deliberate deception.&lt;/p&gt;

&lt;p&gt;The next thing I would measure is whether people find the copy useful—and whether they would reply. This experiment has no human quality or conversion data.&lt;/p&gt;

&lt;p&gt;A clean checker score still leaves an email to edit.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Other writing habits changed too
  &lt;p&gt;After inspecting the outputs, I counted a few recurring patterns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;A: no extra rule&lt;/th&gt;
&lt;th&gt;C: facts only&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;excited&lt;/code&gt;, &lt;code&gt;thrilled&lt;/code&gt;, &lt;code&gt;proud&lt;/code&gt; or &lt;code&gt;delighted&lt;/code&gt; within the first 260 characters of a LinkedIn post&lt;/td&gt;
&lt;td&gt;161 / 202 (79.7%)&lt;/td&gt;
&lt;td&gt;119 / 200 (59.5%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;would you be open&lt;/code&gt;, &lt;code&gt;are you open&lt;/code&gt; or &lt;code&gt;open to a&lt;/code&gt; in a cold email&lt;/td&gt;
&lt;td&gt;108 / 203 (53.2%)&lt;/td&gt;
&lt;td&gt;57 / 202 (28.2%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An em dash in any format&lt;/td&gt;
&lt;td&gt;314 / 605 (51.9%)&lt;/td&gt;
&lt;td&gt;155 / 604 (25.7%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These use all generated first-repeat texts in the relevant format, including unscored ones. They are exploratory frequencies, not measures of bad writing or a way to detect AI authorship. The prompts did not directly ban these expressions.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  What the checks do—and do not—establish
  &lt;p&gt;The reader passed thirty simple synthetic controls: ten supported restatements, ten wrong-price injections and ten wrong-team-size injections. That does not establish accuracy on natural marketing copy or replace human calibration.&lt;/p&gt;

&lt;p&gt;An additional audit found 45 reader claim quotes across 37 original scored texts that were not exact substrings of the generated copy; 26 of those texts were in the first-repeat analysis. Formatting differences and paraphrases can cause this, so it is not a count of incorrect judgments. Unlike the follow-up, the original parser did not require exact-substring quotes. I kept the original scores and separately excluded those 26 judgments: the pooled conclusions for the number ban, facts-only rule and repeated brief stayed the same. This check does not establish that the remaining judgments are correct. The &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/analysis/METHOD_CHANGES.md#saved-data-and-inference-audit--2026-10-06" rel="noopener noreferrer"&gt;audit notes&lt;/a&gt; record the correction and sensitivity checks.&lt;/p&gt;

&lt;p&gt;The original overlap, repetition and staffing checks were chosen after seeing results. The new-company follow-up supports the overlap finding in four selected writers; it does not validate every exploratory threshold. The bootstrap intervals also leave out uncertainty from the AI judge.&lt;/p&gt;

&lt;p&gt;An early automatic invitation check missed valid invitations. The corrected rules and regression cases are in the repository, but invitation rates are excluded from the main findings. The Rackhand Robotics email pair is an illustration, not a validated general claim about missing calls to action.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.kaggle.com/benchmarks/sohee087/which-ai-lies-less-in-marketing-copy" rel="noopener noreferrer"&gt;Public Kaggle benchmark and leaderboard&lt;/a&gt; · &lt;a href="https://www.kaggle.com/benchmarks/tasks/sohee087/which-ai-lies-less-in-marketing-copy" rel="noopener noreferrer"&gt;Task and model runs&lt;/a&gt; · &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench" rel="noopener noreferrer"&gt;Code, saved responses and analysis&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository contains the task, fictional briefs, saved selected responses, file hashes and the full quoted post and emails. Recompute the original analysis without model calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements-analysis.txt
python analysis/reproduce.py
python analysis/validate_submission.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;results/analysis/article_evidence.json&lt;/code&gt; connects the original analysis to saved outputs. The follow-up has &lt;a href="https://github.com/gracelee087/ai-marketing-honesty-bench/blob/master/validation_2026-10-05/README.md" rel="noopener noreferrer"&gt;separate offline reproduction commands&lt;/a&gt;. Re-running the live Kaggle task may use quota.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI assistance: Claude Code assisted the original implementation and analysis. Codex assisted the code review, analysis corrections, follow-up experiment, charts and write-up. The benchmark reader is glm-5. No human grading is claimed.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kagglechallenge</category>
    </item>
    <item>
      <title>Open Call: an agent that says where every hackathon fact came from</title>
      <dc:creator>Sohee Lee </dc:creator>
      <pubDate>Sat, 26 Sep 2026 12:07:55 +0000</pubDate>
      <link>https://dev.to/gracelee087/-open-call-an-agent-that-says-where-every-hackathon-fact-came-from-41m6</link>
      <guid>https://dev.to/gracelee087/-open-call-an-agent-that-says-where-every-hackathon-fact-came-from-41m6</guid>
      <description>&lt;p&gt;&lt;em&gt;Submitted to the DEV × Sanity Challenge, Path One.&lt;/em&gt;&lt;/p&gt;




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

&lt;p&gt;On 20 September 2026 I went looking for a hackathon to go to in Europe. I opened five listing sites and no two of them agreed on what exists.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Listing site&lt;/th&gt;
&lt;th&gt;European hackathons it showed for Oct–Dec 2026&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dev.events&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5 — Junction and CASSINI missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mlh.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3 of its 74 events, all in the UK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;luma.com/tech-europe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4 dates, mostly already past&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hackathon.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0&lt;/strong&gt; — &lt;em&gt;"There is no upcoming hackathons found in 'Europe'"&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openhackathons.org&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;unreadable; the listing never renders without JavaScript&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Between them, fifteen were open.&lt;/p&gt;

&lt;p&gt;The pages that did exist disagreed with themselves. Junction advertises travel grants in the present tense — &lt;em&gt;"Travel grants are available on a limited basis"&lt;/em&gt; — on a page whose grant deadline passed on 6 September; its own application platform says they &lt;em&gt;"have been closed for international participants."&lt;/em&gt; BaselHack puts its dates on the homepage and in a blog post, but not on the page called &lt;strong&gt;Event&lt;/strong&gt;. Odoo gives its cancellation cut-off twice on one page, as 13 November in one section and 14 November in another.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open Call&lt;/strong&gt; is an agent that answers questions about free, in-person hackathons in Europe starting after October 2026, and for every fact it gives you, tells you which page it was read on, whether a newer page has overtaken it, and — the part that mattered most — when nobody says at all.&lt;/p&gt;

&lt;p&gt;The sharpest thing it found, it found in a PDF seventeen pages deep. The CASSINI hackathon site advertises &lt;em&gt;"cash prizes up to 9,000 EUR."&lt;/em&gt; Its Rules of Contest say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;No monetary prize will be awarded in the first stage ('Local Hackathons') by EUSPA.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One team per city reaches the finals. For everyone else there is no EUSPA money at all. A keyword search for "CASSINI prize" returns the 9,000. Both sentences are in the dataset now, the website's marked superseded, and the agent leads with the rules.&lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/H06X7CoVx8A" width="710" height="399"&gt;
  &lt;/iframe&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%2Fpd50iayfqjlu35unmleo.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%2Fpd50iayfqjlu35unmleo.png" alt="The front page: four recorded questions and a box to ask your own" width="800" height="685"&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%2Fasxe5e498uxnh4gthfwq.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%2Fasxe5e498uxnh4gthfwq.png" alt="An answer with the GROQ it ran expanded underneath" width="755" height="900"&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%2Fihvr659fttkha7zud44r.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%2Fihvr659fttkha7zud44r.png" alt="The same answer: one deadline is sourced, the rest are marked not stated" width="800" height="855"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The four example questions on the front page are &lt;strong&gt;recordings, not mock-ups&lt;/strong&gt;: &lt;code&gt;web/data/examples.json&lt;/code&gt; holds the answer text, the tool calls and the GROQ queries exactly as the agent produced them, written by &lt;code&gt;web/scripts/record-examples.mjs&lt;/code&gt;. They are bundled into a statically prerendered page, so opening one costs no API call and no waiting, and each one will show you the GROQ it ran. The text box underneath runs the same agent live against both MCP endpoints.&lt;/p&gt;

&lt;p&gt;Ask it &lt;em&gt;"What do the listing sites get wrong?"&lt;/em&gt; and it will tell you that hackathon.com returned nothing on a day fifteen events were open, and that dev.events omitted a hackathon with 2,000 builders and a €100,000 prize pool three weeks after the last event on its list.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/gracelee087/open-call" rel="noopener noreferrer"&gt;https://github.com/gracelee087/open-call&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;studio/
  schemaTypes/     source.ts, statement.ts, event.ts, sourcedClaim.ts, isUnique.ts
  scripts/seed.ts  every fact, with the reasoning for it in comments
web/
  lib/context.ts   the two-endpoint MCP connection
  app/api/chat/    the agent
  data/            the recorded answers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next.js 16 + AI SDK 7, Claude Sonnet 5, Sanity Studio v6. The root README has the setup and an honest &lt;em&gt;What this does not do&lt;/em&gt; section. TypeGen runs over the three queries that touch the dataset directly and generates their result types beside the script that uses them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Sanity
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The content model is about provenance, not about hackathons
&lt;/h3&gt;

&lt;p&gt;Nothing here stores a fact without storing where it came from.&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="c1"&gt;// A yes/no fact that always says where it came from.&lt;/span&gt;
&lt;span class="nx"&gt;sourcedClaim&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not-stated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="kr"&gt;string&lt;/span&gt;     &lt;span class="c1"&gt;// the qualifier, in the source's own words&lt;/span&gt;
  &lt;span class="nx"&gt;basis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;   &lt;span class="c1"&gt;// the page it was read on&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Three values, not two.&lt;/strong&gt; A page that never mentions travel grants is not a page that says there are none, and collapsing those two into &lt;code&gt;false&lt;/code&gt; is how a dataset starts lying. Every contested field on an event — free entry, students only, travel costs, accommodation, whether a prize is cash — is one of these objects, defined once and reused five times.&lt;/p&gt;

&lt;p&gt;Of the twenty-five claim slots in the dataset, &lt;strong&gt;eleven are &lt;code&gt;not-stated&lt;/code&gt;&lt;/strong&gt; — and every one of them still points at the page that was read and found silent. That distinction is the whole product. It is also the thing I got wrong first: an early version left those slots empty, which reads as "nobody looked" and is exactly the boolean-plus-note design the three values exist to avoid.&lt;/p&gt;

&lt;p&gt;The schema refuses to store a claim that cannot point at a source:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A stated value needs a source. Either set it to "Not stated in the source", or say where you read it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxqnmeumqt8501qcjq48y.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%2Fxqnmeumqt8501qcjq48y.png" alt="Studio: Free to enter set to Yes with its source cleared. The field is flagged and Publish is disabled" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and refuses a prize figure with nothing behind it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A prize figure needs a source. Fill in "Prize is cash" and say where the figure was read.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The collection rules live in the schema rather than in a README: &lt;code&gt;eventStart&lt;/code&gt; before 2026-10-01 is rejected with a message saying why, and &lt;code&gt;format&lt;/code&gt; has no &lt;code&gt;online&lt;/code&gt; option. &lt;code&gt;source.url&lt;/code&gt; and &lt;code&gt;event.slug&lt;/code&gt; validate uniqueness asynchronously, because the seed script finds documents by those fields and patches them in place — a duplicate would silently send the next run's patch to whichever one it read first.&lt;/p&gt;

&lt;p&gt;Three document types and one object:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;What it holds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;source&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One page we read. Who published it, when it was written, &lt;strong&gt;when we read it&lt;/strong&gt;, and which edition it is talking about — the field that catches Wikipedia describing 2025.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;statement&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One thing one source said about one aspect of one event, &lt;strong&gt;quoted verbatim&lt;/strong&gt;, with a verdict: accepted, superseded, incorrect, or not looked at yet.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;event&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One edition. Junction 2026 and Junction 2025 would be two documents.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sourcedClaim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The object above.&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%2Fam9irsvjbqjznpwoeof0.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%2Fam9irsvjbqjznpwoeof0.png" alt="Studio: statements, each quoted verbatim and tied to one event, one field and one source" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Statements come first and event fields are filled from them, never the other way round, so a summary that turns out wrong can be traced back to the sentence it came from.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Context MCP endpoints, merged in the client
&lt;/h3&gt;

&lt;p&gt;Sanity Context serves &lt;strong&gt;one content source per endpoint&lt;/strong&gt;, so the product arrives over two:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;open-call               knowledge base   what the pages actually say
open-call-data-dataset  dataset          filters over what we recorded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both expose a tool named &lt;code&gt;initial_context&lt;/code&gt;, so merging their tool maps would silently drop one of them — along with either the knowledge base outline or the schema overview. Instead both payloads are fetched over HTTP and concatenated into the system prompt, and the tool is left out of the tool set entirely. That is Sanity's documented alternative, and it also removes a round trip from the start of every conversation. The model ends up with five tools and picks the half of the product it needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Which prizes are real cash?"              → groq_query, then knowledge_base_read
"Can I win the 9,000 EUR at a local one?"  → knowledge_base_read, then groq_query
"What can I still apply to?"               → both, three calls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What the Knowledge Base caught that I did not
&lt;/h3&gt;

&lt;p&gt;Nine sources went in: five organiser pages, an application platform, Wikipedia, three listing sites included deliberately so there would be something to disagree with, and the project's own dataset. 72 documents, 9 entries — 73 and 11 after the rebuild described below.&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%2Fr5thajpmps2ariwv46vl.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%2Fr5thajpmps2ariwv46vl.png" alt="Context: the knowledge base's nine sources" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The build raised one conflict, and it was not the one I expected. The Odoo registration page contradicts &lt;strong&gt;itself&lt;/strong&gt; about the cancellation cut-off — 13 November in one section, 14 November in another. I had read that page closely enough to quote from it and never noticed.&lt;/p&gt;

&lt;p&gt;Chasing it down turned up a mistake in my own data. I had recorded &lt;code&gt;applyBy: 2026-11-13&lt;/code&gt; for Odoo. That is not an application deadline at all; it is the cancellation refund cut-off, and I had taken it from a search summary rather than from the page. Resolving the issue wrote an Instruction that shapes every future build. Five more were written by hand, one per source, so the knowledge base carries what each source is worth:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This article describes Junction 2025, not Junction 2026. It was last edited in June 2026 and still has no 2026 content. Never use its dates, prize figures or participant numbers for the 2026 edition.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;These listings are incomplete. Absence from them is not evidence that an event does not exist… Never answer "there are none" or "it is not listed" on the strength of these pages.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5zb7pe92m3b1rvrj14pf.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%2F5zb7pe92m3b1rvrj14pf.png" alt="Context: the Odoo conflict, both sentences side by side, resolved into an instruction" width="800" height="379"&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%2F0i9sg4wv9w8ckcc2xcco.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%2F0i9sg4wv9w8ckcc2xcco.png" alt="Context: the instructions, each tied to the sources it applies to" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next build then wrote an entry I had not asked for — &lt;code&gt;source_reliability&lt;/code&gt;, with a topic for each aggregator omission, the Wikipedia date lag, and the BaselHack inconsistency, plus a hierarchy-of-trust table. The instructions changed the shape of the index, not just its wording. It is the entry the agent reaches for when you ask what the listing sites get wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  And what the dataset caught in the Knowledge Base
&lt;/h3&gt;

&lt;p&gt;This is the part I did not plan and would not have found without both layers.&lt;/p&gt;

&lt;p&gt;After fixing the seed, I read the generated Odoo entry directly over MCP. It says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Applications close &lt;strong&gt;13 November 2026&lt;/strong&gt; [3]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The knowledge base had inherited my error and hardened it into prose, with a footnote pointing at a statement about deposits. Meanwhile the dataset — rebuilt to record both conflicting sentences verbatim and no &lt;code&gt;applyBy&lt;/code&gt; at all — now says something the entry does not: that no source states an application deadline for this event.&lt;/p&gt;

&lt;p&gt;So the two layers caught each other. The knowledge base found the contradiction I had read past; the structured dataset found the fact the knowledge base had invented. Neither alone was enough, and a keyword search over either would have returned the wrong date with confidence. The system prompt now carries the rule that came out of it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A date on a page is only the deadline the page says it is. A refund cut-off, a cancellation date and a ticket sale ending are not application deadlines.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Asked the same question today, the agent names the trap instead of falling into it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Odoo's registration page discusses a cancellation/refund cutoff, not an application deadline — and even that is internally inconsistent… No source states an actual application deadline for Odoo Hackathon #6, so I'm not going to present either of those dates as one."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The entry itself took a rebuild to fix, and the rebuild made a new mistake. It now says &lt;em&gt;"No application deadline is stated anywhere by the organiser."&lt;/em&gt; But the rebuilt CASSINI entry took a line from the organisers' recruitment page, &lt;em&gt;"12th Hackathon Applications closed"&lt;/em&gt;, and the next recorded answer told a participant that CASSINI #12 was closed to them. The line sits next to a link to the organiser application form. It is about organisations applying to host a local hackathon, not about participants. The dataset has no application date and no registration status for CASSINI, so the answer had nothing behind it. One more instruction, scoped to that page, and the entry now reads &lt;em&gt;"Applications for local organisers for Hackathon #12 are now closed."&lt;/em&gt; The recorded answer says CASSINI is &lt;em&gt;"Unclear for participants."&lt;/em&gt; That is the most the sources support.&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%2Fb13e1p692tna0zttyi5q.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%2Fb13e1p692tna0zttyi5q.png" alt="Context: the rebuilt knowledge base, eleven entries including a cross-event one" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sanity Project Details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project ID: &lt;code&gt;dmar00cc&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset: &lt;code&gt;production&lt;/code&gt;&lt;/strong&gt; — public, no token required:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://dmar00cc.api.sanity.io/v2024-01-01/data/query/production?query=*[_type=="event"]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;37 documents: 13 sources, 5 events, 19 statements. Two queries worth running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// every claim nobody actually states, with the page we read it on
*[_type=="event"]{name, "silent": [free, studentsOnly, travelCovered, accommodation,
  prizeIsCash][value=="not-stated"]{note, "readOn": basis-&amp;gt;url}}

// every source that has been overtaken or contradicted
*[_type=="statement" &amp;amp;&amp;amp; verdict in ["superseded","incorrect"]]{
  quote, verdict, verdictNote, "said by": source-&amp;gt;title}
&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%2Fd0pumybn822ihx43uebl.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%2Fd0pumybn822ihx43uebl.png" alt="sanity.io/manage: project Open Call, ID dmar00cc" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What this does not do
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Five events.&lt;/strong&gt; Enough to show the modelling and to find real disagreements, not enough to plan a year around. Every listing site above showed five or fewer, which is rather the point — but it is still five.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No times, only dates.&lt;/strong&gt; The hour and the timezone are not modelled.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;English sources only.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The knowledge base does not catch numeric disagreement.&lt;/strong&gt; Across two builds in two unrelated domains it raised conflicts over wording and never over two sources giving different figures for the same thing. The &lt;code&gt;statement&lt;/code&gt; layer exists partly to cover that.&lt;/li&gt;
&lt;/ul&gt;

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




&lt;p&gt;Built with Claude Code. The demo video is a screen recording of the app, edited with &lt;a href="https://hyperframes.heygen.com" rel="noopener noreferrer"&gt;HyperFrames&lt;/a&gt; together with the screenshots above. Not investment advice, and not a substitute for reading the organiser's page before you book a flight. That is rather the point.&lt;/p&gt;

</description>
      <category>sanitychallenge</category>
    </item>
    <item>
      <title>CSS Art: Chimaek 🍗🍺</title>
      <dc:creator>Sohee Lee </dc:creator>
      <pubDate>Sun, 02 Aug 2026 20:07:50 +0000</pubDate>
      <link>https://dev.to/gracelee087/css-art-chimaek-2cob</link>
      <guid>https://dev.to/gracelee087/css-art-chimaek-2cob</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, CSS Art&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;h3&gt;
  
  
  🪩 WELCOME TO CHIMAEK SOUL CLUB!
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Open every night. No dress code. One item on the menu.&lt;/em&gt; 🍗🍺&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chimaek (치맥)&lt;/strong&gt; is a Korean portmanteau of &lt;strong&gt;"Chi" (Chicken)&lt;/strong&gt; and &lt;strong&gt;"Maek" (Maekju, Korean for beer).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And it is my &lt;strong&gt;soul food&lt;/strong&gt; — in every sense of the words.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  🍻 "Let's have Chimaek."
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Korea, this simple sentence means much more than &lt;em&gt;"Let's eat fried chicken and drink beer."&lt;/em&gt;&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;💪 &lt;strong&gt;"You worked hard today."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;😌 &lt;strong&gt;"Let's take a break."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🥂 &lt;strong&gt;"Let's enjoy this moment together."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Chimaek is...&lt;/p&gt;

&lt;p&gt;🍗 the reward after a long day at work&lt;br&gt;
🫂 the comfort after exhaustion&lt;br&gt;
🎉 a way to celebrate good news&lt;br&gt;
👨‍👩‍👧 a reason to gather with friends and family&lt;br&gt;
🌙 a chance to forget your worries, even if it's just for one evening&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%2F8x1qp3w9ljoodc9ses1v.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%2F8x1qp3w9ljoodc9ses1v.jpg" alt="A typical Korean street filled with fried chicken restaurants" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One street, countless Chimaek restaurants — an everyday sight in many Korean cities. Created with AI · © 2026 [Sohee Lee]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For many Koreans, Chimaek is more than a favorite meal—it's a small ritual that marks the end of a busy day and the beginning of a moment to relax.&lt;/p&gt;

&lt;p&gt;Koreans love fried chicken so much that busy neighborhoods are often lined with chicken restaurants. It's completely normal to find several fried chicken restaurants on a single block, each with its own recipe, loyal customers, and atmosphere.&lt;/p&gt;

&lt;p&gt;That feeling of comfort, warmth, and togetherness is what I wanted to capture through this CSS artwork. ✨&lt;/p&gt;
&lt;h2&gt;
  
  
  🎬 Demo
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/editor/gracelee087/embed/019fc3ee-0658-7a6f-803f-8e94dd74bcd3?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SOUL CLUB&lt;/strong&gt; is open. 🪩 Doors at sunset, lights already running at 125 BPM.&lt;/p&gt;

&lt;p&gt;When the sun goes down, SOUL CLUB comes alive—it's what the end of a long day feels like.&lt;/p&gt;

&lt;p&gt;🎧 The room does everything. Beams sweep, the floor pulses, the strobe lands on every beat.&lt;br&gt;
💪 The bucket is on stage, because tonight it's the main event.&lt;br&gt;
😌 The chicken never moves. It isn't performing — it's waiting for you.&lt;br&gt;
🥂 Two glasses lean in, clink, and drift apart. Once a second, forever.&lt;/p&gt;
&lt;h2&gt;
  
  
  🛠️ Journey
&lt;/h2&gt;

&lt;p&gt;Instead of drawing one realistic illustration, I wanted to capture the &lt;em&gt;feeling&lt;/em&gt; of Chimaek — so I gave it a room, a tempo, and a toast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🍗 Building the chicken.&lt;/strong&gt; Each piece is a single element wearing a &lt;code&gt;clip-path&lt;/code&gt; polygon with 20 points at uneven radii. Fried chicken is defined by its craggy silhouette far more than by its shading, so the outline had to do the work. Three grades of crust share the same 18-degree spokes and differ only in how far the long and short radii spread apart: a wide spread reads as shattered batter, a narrow one as smooth crisp skin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🪩 Building the room.&lt;/strong&gt; The sweeping club beams are a &lt;code&gt;repeating-conic-gradient&lt;/code&gt; pinned to a point above the frame and rotated — which is genuinely what a lighting rig looks like from the floor. &lt;code&gt;mask-image&lt;/code&gt; fades them out before they reach the ground, and &lt;code&gt;mix-blend-mode&lt;/code&gt; makes the coloured light sit &lt;em&gt;on&lt;/em&gt; the chicken instead of next to it. The rest is gradients, &lt;code&gt;border-radius&lt;/code&gt;, layered &lt;code&gt;box-shadow&lt;/code&gt;s and pseudo-elements, all sized in &lt;code&gt;vmin&lt;/code&gt; so the scene scales to any screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🎵 Building the tempo.&lt;/strong&gt; Everything the room does runs off one custom property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--beat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;.48s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;--bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.92s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c"&gt;/* 125 BPM */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strobe, the floor pulse and the beam sweep are all whole multiples of it, so the lighting can never drift out of time with itself. Change that one number and the whole club changes tempo. The toast runs on its own 1-second clock, deliberately &lt;em&gt;off&lt;/em&gt; the beat, because two people clinking glasses are never in time with the music.&lt;/p&gt;

&lt;h3&gt;
  
  
  😳 Three things that surprised me
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;😀 Faces appear whether you want them or not.&lt;/strong&gt; My first crust texture was a dark oval with a curved stroke underneath, repeated on every piece — and all of them ended up smiling at me. I replaced the ovals with hard-edged chips and the chicken turned into cartoon cheese instead, because a solid patch on a yellow surface reads as a &lt;em&gt;hole&lt;/em&gt;. What finally worked was the same patch faded out at the edges. Two matched dots with a curve underneath will read as a face no matter what you intended.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🌗 Contrast matters more than detail.&lt;/strong&gt; The first version had a cream background, and since fried chicken and beer are both amber, the food dissolved into the glasses. Moving the whole scene into a dark room fixed the chicken, the foam and the label in a single change — and that's why this piece ended up at night.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🤏 Restraint reads better than energy.&lt;/strong&gt; An early version had the glasses swinging halfway across the poster and crashing together in a burst of foam. It looked exciting and it was wrong: it read as a big night out. So I cut the wind-up, took the tilt from 17 degrees down to 7, and moved the glasses close enough that they only have to lean. Making the movement smaller is what finally made it feel like Chimaek.&lt;/p&gt;

&lt;h3&gt;
  
  
  ♿ Accessibility
&lt;/h3&gt;

&lt;p&gt;The whole scene is a single &lt;code&gt;role="img"&lt;/code&gt; with a written description, so a screen reader gets one object instead of 121 empty divs. &lt;code&gt;prefers-reduced-motion&lt;/code&gt; stops every animation.&lt;/p&gt;

&lt;h3&gt;
  
  
  🌃 What's next
&lt;/h3&gt;

&lt;p&gt;I'd like to try the same approach on a whole street — a Korean block at night with a glowing chicken sign in every building, which is what the photo above actually looks like.&lt;/p&gt;

&lt;p&gt;📄 Code is MIT licensed.&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>css</category>
      <category>devchallenge</category>
    </item>
    <item>
      <title>Your Certificate Folder Is Not a Portfolio (Collect Evidence Instead)</title>
      <dc:creator>Sohee Lee </dc:creator>
      <pubDate>Sat, 25 Jul 2026 18:29:20 +0000</pubDate>
      <link>https://dev.to/gracelee087/your-certificate-folder-is-not-a-portfolio-collect-evidence-instead-3965</link>
      <guid>https://dev.to/gracelee087/your-certificate-folder-is-not-a-portfolio-collect-evidence-instead-3965</guid>
      <description>&lt;p&gt;Confession: I used to be a certificate collector.&lt;/p&gt;

&lt;p&gt;Course completed — screenshot. Webinar attended — badge. Another PDF into the folder called "Career" that no recruiter will ever open. It felt productive. It was mostly decoration.&lt;/p&gt;

&lt;p&gt;Then I noticed something about how people actually get hired, and it changed how I spend my study time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The difference between proof of attendance and proof of ability
&lt;/h2&gt;

&lt;p&gt;Most certificates prove one thing: you were present. You watched the videos, you clicked next, you passed a quiz you could retake unlimited times.&lt;/p&gt;

&lt;p&gt;A recruiter reading "Completed 12 online courses" learns almost nothing about you, because completion measures patience, not ability.&lt;/p&gt;

&lt;p&gt;What actually transfers is evidence — an artifact that could not exist unless you can do the thing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A deployed project with a URL that works&lt;/li&gt;
&lt;li&gt;A hands-on credential where you performed real tasks in a live environment&lt;/li&gt;
&lt;li&gt;A blog post explaining something you built and what broke along the way&lt;/li&gt;
&lt;li&gt;A competition entry, even one that lost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Evidence has a property certificates lack: it survives questions. "Tell me about this" is a fun conversation when there is a real thing behind it, and a very short one when there is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  My filter now: can it be faked by watching videos?
&lt;/h2&gt;

&lt;p&gt;Before I commit time to any learning resource, I ask one question: could someone earn this outcome purely by watching videos?&lt;/p&gt;

&lt;p&gt;If yes, it is a certificate. If no, it is evidence.&lt;/p&gt;

&lt;p&gt;This filter is why I switched to hands-on formats. I recently did a Microsoft Applied Skills assessment — instead of multiple-choice questions, you get dropped into a live lab and have to actually deploy a model, create an AI agent, and test it. No lab performance, no credential. (It is also completely free, which as a student is the correct price: &lt;a href="https://learn.microsoft.com/credentials/applied-skills/get-started-developing-agents-in-microsoft-foundry?wt.mc_id=studentamb_519411" rel="noopener noreferrer"&gt;https://learn.microsoft.com/credentials/applied-skills/get-started-developing-agents-in-microsoft-foundry?wt.mc_id=studentamb_519411&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I am not saying this particular credential is magic. I am saying the FORMAT is the point: assessments where you do the thing are worth ten where you recognize the correct answer about the thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 1:1 rule
&lt;/h2&gt;

&lt;p&gt;Here is the habit that fixed my folder-of-PDFs problem:&lt;/p&gt;

&lt;p&gt;For every course or credential, produce one artifact that exists outside the platform.&lt;/p&gt;

&lt;p&gt;Finished a module on AI agents? Build one tiny agent that solves a real annoyance in your life and write 500 words about it. Learned some cloud basics? Deploy literally anything with a public URL. The course gets you knowledge; the artifact makes the knowledge visible.&lt;/p&gt;

&lt;p&gt;My ratio used to be 12 courses : 0 artifacts. The folder was full and the portfolio was empty.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you are starting from zero
&lt;/h2&gt;

&lt;p&gt;The good news: as a student, the evidence pipeline costs nothing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free cloud to deploy real things (no credit card): &lt;a href="https://azure.microsoft.com/free/students?wt.mc_id=studentamb_519411" rel="noopener noreferrer"&gt;https://azure.microsoft.com/free/students?wt.mc_id=studentamb_519411&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Free structured courses with actual labs: &lt;a href="https://learn.microsoft.com/training?wt.mc_id=studentamb_519411" rel="noopener noreferrer"&gt;https://learn.microsoft.com/training?wt.mc_id=studentamb_519411&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Free places to write about what you built: you are reading one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick one annoyance in your life, solve it badly with technology, and document it honestly. That single artifact will do more for you than the next five certificates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Am I wrong?
&lt;/h2&gt;

&lt;p&gt;Genuine question for the comments: has a plain completion certificate ever directly helped you get an interview or a job? I am collecting counterexamples — maybe the folder deserves more respect than I am giving it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: Links in this post include my Microsoft Student Ambassadors contributor ID, which tracks my progress toward the program. I do not earn anything from clicks. All opinions are my own.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>beginners</category>
      <category>learning</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Built an AI Agent to Decode Scary German Bureaucracy Letters (as an International Student)</title>
      <dc:creator>Sohee Lee </dc:creator>
      <pubDate>Sat, 25 Jul 2026 18:16:20 +0000</pubDate>
      <link>https://dev.to/gracelee087/i-built-an-ai-agent-to-decode-scary-german-bureaucracy-letters-as-an-international-student-3dne</link>
      <guid>https://dev.to/gracelee087/i-built-an-ai-agent-to-decode-scary-german-bureaucracy-letters-as-an-international-student-3dne</guid>
      <description>&lt;p&gt;Every international student in Germany knows the fear: a letter arrives in your mailbox, it's in formal German, it has a deadline, and it's definitely not good news. Rundfunkbeitrag? Krankenkasse? Something from the Auslaenderbehoerde?&lt;/p&gt;

&lt;p&gt;My German is... a work in progress. So instead of panicking (my usual strategy), I built an AI agent that reads these letters for me.&lt;/p&gt;

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

&lt;p&gt;I paste in the text of any official German letter, and th&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Summarizes what the letter actually wants, in plain English&lt;/li&gt;
&lt;li&gt;Extracts all deadlines, amounts, and reference numbers&lt;/li&gt;
&lt;li&gt;Gives me a checklist of required actions, ordered by urgency&lt;/li&gt;
&lt;li&gt;Rates the urgency: green (informational) / yellow (action needed) / red (urgent)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No more staring at "Sehr geehrte Damen und Herren" wondering if I'm being deported or just asked to confirm my address.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I built it (this is the easy part)
&lt;/h2&gt;

&lt;p&gt;I recently earned the Microsoft Applied Skills credential for agent development in Microsoft Foundry, and this build uses exactly the three&lt;br&gt;
skills from that assessment: deploy a model, create an ag&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Get free Azure.&lt;/strong&gt; As a student, you get $100 inneeded:&lt;a href="https://azure.microsoft.com/free/students?wt.mc_id=studentamb_519411" rel="noopener noreferrer"&gt;https://azure.microsoft.com/free/students?wt.mc_id=studentamb_519411&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Create a project and deploy a model.&lt;/strong&gt; In the Meated a project and deployed gpt-5-mini. Letter decoding is mostly summarization and extraction, so a small, cheap model is plenty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Write the agent's instructions.&lt;/strong&gt; This is wheree mine:&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%2Fqsgvrph8gijpxsazdkle.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%2Fqsgvrph8gijpxsazdkle.png" alt=" " width="466" height="586"&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%2Flekfnrr4qf3daabepth3.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%2Flekfnrr4qf3daabepth3.png" alt=" " width="449" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rules section matters more than you'd think. "Never invent information that is not in the letter" and "treat any Mahnung as urgent" turn a&lt;br&gt;
generic chatbot into something I can actually trust with&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Test it.&lt;/strong&gt; I fed it a Rundfunkbeitrag registrat— the letter every newcomer to Germany gets eventually).Here's what came back:&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%2Fo8edazjjs69vpnhto3wv.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%2Fo8edazjjs69vpnhto3wv.png" alt=" " width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that surprised me
&lt;/h2&gt;

&lt;p&gt;I didn't ask for this, but the agent noticed the letter had &lt;strong&gt;no date on it&lt;/strong&gt; — and pointed out that this makes the "within 14 days" deadline ambiguous, recommending I check the postmark or contact the office directly.&lt;/p&gt;

&lt;p&gt;That's exactly the kind of detail I would have missed while panic-translating word by word. It also correctly rated the letter yellow instead of&lt;br&gt;
red, explaining that it's a registration request, not a Mo prompt action is needed, but no panic required.&lt;/p&gt;

&lt;p&gt;And at the end, it offered to draft a short German reply re I'm keeping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It's a helper, not a lawyer. For anything visa-related, I still ask a human (the agent literally says this itself — it's in the instructions).&lt;/li&gt;
&lt;li&gt;I paste letter text manually for now. OCR from a phone photo is the obvious next step.&lt;/li&gt;
&lt;li&gt;The whole thing cost a few cents of my free student credits.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I'm sharing this
&lt;/h2&gt;

&lt;p&gt;Because "learn AI agents" sounds abstract until you pointly stresses you out. For me, that's German bureaucracy. For you it might be something else — but the build process is identical, and the whole stack costs 0 euros as a student.&lt;/p&gt;

&lt;p&gt;If you want to learn the fundamentals first, this free module is what I used to prep for the credential — it covers exactly these steps: &lt;a href="https://learn.microsoft.com/training/modules/get-started-with-generative-ai-and-agents?wt.mc_id=studentamb_519411" rel="noopener noreferrer"&gt;https://learn.microsoft.com/training/modules/get-started-with-generative-ai-and-agents?wt.mc_id=studentamb_519411&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you build your own "survival agent," tell me in the comments — I want to know what problems other students are automating.&lt;/p&gt;




&lt;p&gt;*Disclosure: Links in this post include my Microsoft Student Ambassadors contributor ID, which tracks my progress toward the program. I don't&lt;br&gt;
earn anything from clicks. All opinions (and bureaucracy&lt;/p&gt;

</description>
      <category>ai</category>
      <category>showdev</category>
      <category>beginners</category>
      <category>azure</category>
    </item>
    <item>
      <title>I Got a Microsoft Certification for Free — Here's How (Applied Skills Review)</title>
      <dc:creator>Sohee Lee </dc:creator>
      <pubDate>Sat, 25 Jul 2026 17:42:29 +0000</pubDate>
      <link>https://dev.to/gracelee087/i-got-a-microsoft-certification-for-free-heres-how-applied-skills-review-5cf9</link>
      <guid>https://dev.to/gracelee087/i-got-a-microsoft-certification-for-free-heres-how-applied-skills-review-5cf9</guid>
      <description>&lt;p&gt;I recently earned a &lt;strong&gt;Microsoft Applied Skills credential&lt;/strong&gt; — completely free, taken online from home. Here's everything you need to know.&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%2Fggkc8gqw38r4pxc6o7cv.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%2Fggkc8gqw38r4pxc6o7cv.png" alt=" " width="747" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Applied Skills?
&lt;/h2&gt;

&lt;p&gt;Applied Skills are Microsoft's hands-on credentials. Unlike traditional multiple-choice exams, you complete real tasks in a live lab environment. When you pass, you get a shareable badge for LinkedIn and an online-verifiable certificate. And the best part: &lt;strong&gt;the assessment is free&lt;/strong&gt; — no exam fees like the $99+ you'd pay for a full certification.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one I took: Get Started Developing Agents in Microsoft Foundry
&lt;/h2&gt;

&lt;p&gt;Everyone is talking about AI agents right now, but most people have never actually built one. This credential walks you through the full basics: deploying a model, creating an agent, and testing it — all inside Microsoft Foundry. It took me about a week of casual studying.&lt;/p&gt;

&lt;p&gt;You can see the credential details here: &lt;a href="https://learn.microsoft.com/credentials/applied-skills/get-started-developing-agents-in-microsoft-foundry?wt.mc_id=studentamb_519411" rel="noopener noreferrer"&gt;https://learn.microsoft.com/credentials/applied-skills/get-started-developing-agents-in-microsoft-foundry?wt.mc_id=studentamb_519411&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prepare (step by step)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Complete the free prep module on Microsoft Learn — it covers exactly what the lab tests: &lt;a href="https://learn.microsoft.com/training/modules/get-started-with-generative-ai-and-agents?wt.mc_id=studentamb_519411" rel="noopener noreferrer"&gt;https://learn.microsoft.com/training/modules/get-started-with-generative-ai-and-agents?wt.mc_id=studentamb_519411&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If you're a student, grab a free Azure account for hands-on practice (no credit card required, $100 in credits): &lt;a href="https://azure.microsoft.com/free/students?wt.mc_id=studentamb_519411" rel="noopener noreferrer"&gt;https://azure.microsoft.com/free/students?wt.mc_id=studentamb_519411&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Once you're ready, launch the assessment directly from the credential page — it runs in your browser as an interactive lab.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tips from my attempt
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Start&lt;/strong&gt; on the learning module so your progress gets tracked&lt;/li&gt;
&lt;li&gt;Don't skim the lab exercises — the assessment evaluates three things: deploy a model, create an agent, test the agent. Practice all three&lt;/li&gt;
&lt;li&gt;The lab takes a few minutes to load — that's normal, don't panic&lt;/li&gt;
&lt;li&gt;If you fail, you can retake it (there's a 72-hour cooldown between attempts), so there's zero pressure&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Is it worth it?
&lt;/h2&gt;

&lt;p&gt;For students: absolutely. It's free, it's hands-on, and "built and deployed an AI agent" is a much stronger CV line than another online course certificate. Next, I'm planning a Copilot-related Applied Skills — if you're new to Copilot, start here: &lt;a href="https://learn.microsoft.com/copilot?wt.mc_id=studentamb_519411" rel="noopener noreferrer"&gt;https://learn.microsoft.com/copilot?wt.mc_id=studentamb_519411&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Questions? Drop them in the comments — happy to help! 🚀&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>ai</category>
      <category>beginners</category>
      <category>career</category>
    </item>
    <item>
      <title>Myeongdong 3D — I built a talking 3D copy of my favourite Seoul district</title>
      <dc:creator>Sohee Lee </dc:creator>
      <pubDate>Sun, 12 Jul 2026 20:10:21 +0000</pubDate>
      <link>https://dev.to/gracelee087/myeongdong-3d-i-built-a-talking-3d-copy-of-my-favourite-seoul-district-50ip</link>
      <guid>https://dev.to/gracelee087/myeongdong-3d-i-built-a-talking-3d-copy-of-my-favourite-seoul-district-50ip</guid>
      <description>&lt;h1&gt;
  
  
  Myeongdong 3D — I built a talking 3D copy of my favourite Seoul district
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/weekend-2026-07-09"&gt;DEV Weekend Challenge: Passion Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Myeongdong 3D&lt;/strong&gt; is a GPS walking audio guide for Seoul's most-loved shopping district — inside a hand-built 3D copy of the real city.&lt;/p&gt;

&lt;p&gt;I call her &lt;strong&gt;Tobaggi (토박이)&lt;/strong&gt; — the Korean word for someone born and raised in a neighbourhood, who never left. Because that's exactly what this guide is: a local who knows every alley, and can't wait to show you.&lt;/p&gt;

&lt;p&gt;You come out of Myeongdong Station Exit 6, put your earphones in, and just walk. A warm voice walks with you. Pass the dumpling house that's been steaming mandu since 1966 and she tells you. Pass Korea's first Gothic cathedral and she tells you why protesters once felt safe on its steps. Wander off your route and she gently calls you back.&lt;/p&gt;

&lt;p&gt;My passion behind this: &lt;strong&gt;I love Myeongdong, and it hurts me watching tourists walk straight past its best stories.&lt;/strong&gt; They queue at one famous restaurant from TikTok, buy sheet masks, and leave — never learning that the street they're standing on was Seoul's Montmartre in the 1950s, or that the department store on the corner was Korea's very first. This app is my love letter to a neighbourhood: a guide that makes sure no one walks past the good parts ever again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it (no GPS needed):&lt;/strong&gt; pick a themed course — 🍜 Foodie Tour, 💄 K-Beauty All-In (all 66 cosmetics shops in the district!), 📸 Culture &amp;amp; History, or 🎯 build your own — and press &lt;strong&gt;Start Walk&lt;/strong&gt;. An avatar walks the real streets for you while the guide talks. If you're actually &lt;em&gt;in&lt;/em&gt; Myeongdong, switch to Live GPS and the same guide follows your real position.&lt;/p&gt;

&lt;p&gt;And when the walk is over, the part I'm proudest of: &lt;strong&gt;📷 snap photos as you go&lt;/strong&gt; (each one stamped with the place, the time, and the &lt;em&gt;actual Seoul weather at that moment&lt;/em&gt;), then press &lt;strong&gt;🎞 Make my album&lt;/strong&gt;. Gemini &lt;em&gt;looks at your photos&lt;/em&gt; — not the location tags, the actual pixels — and writes a voiceover like a friend flipping through the album with you ("I still laugh at how seriously you posed under that sunny sky…"), and ElevenLabs tells it over a paper scrapbook of taped-down polaroids. Your photos never leave your browser except for that one narration request — nothing is stored on any server.&lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Tjdt3NTWG9Q"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://myeongdong-3d.netlify.app" rel="noopener noreferrer"&gt;https://myeongdong-3d.netlify.app&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%2Fqosavs3zkpnhb1k56e6l.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%2Fqosavs3zkpnhb1k56e6l.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fna4wtjpmksw2rnkmblva.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%2Fna4wtjpmksw2rnkmblva.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvf4xwxh8h6ecvvdhgtwl.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%2Fvf4xwxh8h6ecvvdhgtwl.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fudxrnohuujs7z17wd82i.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%2Fudxrnohuujs7z17wd82i.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few things to try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The city matches Seoul &lt;strong&gt;right now&lt;/strong&gt; — real sun position (windows light up after dark) and live weather (if it's raining in Seoul, it rains in the app). Add &lt;code&gt;?time=night&lt;/code&gt; or &lt;code&gt;?wx=rain&lt;/code&gt; to the URL to time-travel.&lt;/li&gt;
&lt;li&gt;Click 🎬 on any place in the sidebar for a YouTuber's video of it — know the place &lt;em&gt;before&lt;/em&gt; you walk in. All 116 spots have one.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;My Picks&lt;/strong&gt;, search &lt;em&gt;any&lt;/em&gt; place on Google (try "Gentle Monster") and add it — &lt;strong&gt;Gemini writes a brand-new guide script for it on the spot&lt;/strong&gt;, and ElevenLabs speaks it when you arrive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change your mind mid-walk.&lt;/strong&gt; Spot something interesting while walking? Search it and tap ＋ — the route re-plans from where you're standing and detours there, without restarting the tour. Missed something the guide said? &lt;strong&gt;‹ Prev&lt;/strong&gt; replays it.&lt;/li&gt;
&lt;li&gt;📷 snap a few photos during the walk, then open &lt;strong&gt;🎞 My Album&lt;/strong&gt; and press &lt;em&gt;Make my album&lt;/em&gt; — and listen to Gemini describe what's actually in your pictures.&lt;/li&gt;
&lt;li&gt;Every spot card shows the Korean address with a copy button — made to be shown to a taxi driver.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;The city itself is hand-made.&lt;/strong&gt; Seoul has no public photorealistic 3D data (Google's 3D tiles skip Korea entirely — I learned this the hard way), so I built the district from scratch: 3,535 real building footprints from OpenStreetMap, extruded to their real heights in Three.js, with procedurally generated window grids and colourful street-level storefronts, at real-world metre scale. Cheonggyecheon stream flows through the north of the map — actually flows, with animated current.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Walking is real routing.&lt;/strong&gt; Courses are solved with Dijkstra over the OSM street graph, so the avatar (and you, in GPS mode) always follow real, connected streets from Myeongdong Station Exit 6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The narration is a two-AI pipeline:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✍️ &lt;strong&gt;Google AI (Gemini 3.5 Flash)&lt;/strong&gt; is the &lt;em&gt;writer&lt;/em&gt; — and the &lt;em&gt;eyes&lt;/em&gt;. Every guide script in the app — 116 attractions &amp;amp; restaurants, 66 K-Beauty shops — was written by Gemini from Korean tourism-board descriptions. It's not just build-time: add any Google-searched place to My Picks and a serverless function asks Gemini for a fresh guide script, live. And the trip album uses &lt;strong&gt;Gemini's vision&lt;/strong&gt;: your photos are sent as pixels, and it writes each line from what's actually in the frame — the food you're holding, the pose, the neon — weaving in the real weather and time each shot was taken (the location tag is treated as just a hint, because travellers photograph whatever they love, wherever they stand).&lt;/li&gt;
&lt;li&gt;🎙️ &lt;strong&gt;ElevenLabs&lt;/strong&gt; is the &lt;em&gt;voice&lt;/em&gt;. &lt;strong&gt;Amelia&lt;/strong&gt; — the most-used voice on all of ElevenLabs, an enthusiastic British storyteller — narrates spots museum-style, tells zone-based local history between stops (each area of the map has its own true stories), warns you when you drift off-route, replays anything on ‹ Prev, and tells your photo album like a friend. The demo path is pre-baked to mp3 for instant playback; everything else is live TTS with a browser-voice fallback so the guide never goes silent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real data everywhere:&lt;/strong&gt; Korea TourAPI (places, photos, Korean addresses), Google Places (real opening hours), YouTube Data API (preview videos, news channels filtered out — you get vloggers' experiences, not headlines), Open-Meteo (live Seoul weather), and the actual solar position computed from Seoul's clock.&lt;/p&gt;

&lt;p&gt;Built over the weekend with an AI pair-programmer (Claude Code) — and an unreasonable amount of love for one square kilometre of Seoul.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best use of Google AI&lt;/strong&gt; — Gemini 3.5 Flash writes every word the guide says: the full script pipeline at build time, live script generation for user-added places at runtime (&lt;code&gt;/api/narrate&lt;/code&gt;), and &lt;strong&gt;multimodal vision&lt;/strong&gt; for the trip album (&lt;code&gt;/api/album&lt;/code&gt;) — it looks at the traveller's actual photos and writes a personal voiceover from what's in each frame, plus the weather and hour each photo was taken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best use of ElevenLabs&lt;/strong&gt; — the entire product is a voice: museum-style narrations, location-aware history, navigation guidance ("you've wandered off the route"), instant ‹ Prev replay, and a narrated photo-album finale — all spoken by Amelia, with a pre-baked demo path so judging never depends on live credits.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;명동을 사랑하는 마음으로 — with love, for Myeongdong.&lt;/em&gt; 🇰🇷&lt;/p&gt;

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