<?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: L. Cordero</title>
    <description>The latest articles on DEV Community by L. Cordero (@earlgreyhot1701d).</description>
    <link>https://dev.to/earlgreyhot1701d</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%2F3683045%2F745698c0-b6f4-42ea-96e9-44a671fa69e0.png</url>
      <title>DEV Community: L. Cordero</title>
      <link>https://dev.to/earlgreyhot1701d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/earlgreyhot1701d"/>
    <language>en</language>
    <item>
      <title>Who Named This ReAct? I'd Like to Speak to the Manager.</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Sat, 08 Aug 2026 05:00:13 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/who-named-this-react-id-like-to-speak-to-the-manager-4akg</link>
      <guid>https://dev.to/earlgreyhot1701d/who-named-this-react-id-like-to-speak-to-the-manager-4akg</guid>
      <description>&lt;p&gt;This week I started the Agentic Engineer Nanodegree through &lt;a href="https://aws.amazon.com/about-aws/our-impact/scholars/" rel="noopener noreferrer"&gt;AWS AI &amp;amp; ML Scholars&lt;/a&gt;, the next step in what has become my increasingly formal attempt to understand the things I started building before I knew enough to understand all of them.&lt;/p&gt;

&lt;p&gt;Somewhere in the coursework, ReAct appeared.&lt;/p&gt;

&lt;p&gt;Record scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ReAct?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There was an immediate sense of déjà vu, although not for the reason the curriculum intended.&lt;/p&gt;

&lt;p&gt;I know React. At this point I've built enough React frontends that the word belongs to the ordinary furniture of my still-young development vocabulary. &lt;a href="https://github.com/earlgreyhot1701D/beyondthedocket" rel="noopener noreferrer"&gt;Beyond the Docket&lt;/a&gt; used React with Vite and TypeScript. &lt;a href="https://github.com/earlgreyhot1701D/real-door" rel="noopener noreferrer"&gt;RealDoor&lt;/a&gt; quite literally labels the browser layer of its architecture "React UI." Several of my Next.js projects are built from React components.&lt;/p&gt;

&lt;p&gt;I'm still early enough in this building journey that I regularly encounter concepts for the first time, but React wasn't one of them. React and I had been formally acquainted.&lt;/p&gt;

&lt;p&gt;Frontend React itself has had considerably longer to establish its claim to the name. According to the &lt;a href="https://react.dev/versions" rel="noopener noreferrer"&gt;official React history&lt;/a&gt;, Facebook open-sourced it on May 29, 2013.&lt;/p&gt;

&lt;p&gt;So when a course on agentic engineering presented something called &lt;strong&gt;ReAct&lt;/strong&gt;—capital A, entirely different neighborhood—I wasn't thinking about a 2022 research paper or the history of agent reasoning patterns.&lt;/p&gt;

&lt;p&gt;I was thinking:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Wait. React?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Except apparently not &lt;strong&gt;that&lt;/strong&gt; React.&lt;/p&gt;

&lt;p&gt;Who named this ReAct? I'd like to speak to the manager.&lt;/p&gt;

&lt;p&gt;Then came the explanation.&lt;/p&gt;

&lt;p&gt;ReAct combines &lt;strong&gt;reasoning and acting&lt;/strong&gt;. A language model can reason about what it should do, take an action in an external environment, observe what happened, and use that information to continue reasoning about the task.&lt;/p&gt;

&lt;p&gt;Reason. Act. Observe. Repeat.&lt;/p&gt;

&lt;p&gt;Okay. Fine. The name makes sense.&lt;/p&gt;

&lt;p&gt;I absolutely had to keep looking for the manager anyway.&lt;/p&gt;

&lt;p&gt;The trail led to the research behind &lt;a href="https://react-lm.github.io/" rel="noopener noreferrer"&gt;ReAct: Synergizing Reasoning and Acting in Language Models&lt;/a&gt;, where Shunyu Yao and his collaborators explored what happens when reasoning and acting—capabilities that had largely been studied separately—are combined.&lt;/p&gt;

&lt;p&gt;So this wasn't somebody in the React ecosystem deciding that capitalization could carry the entire burden of technical differentiation.&lt;/p&gt;

&lt;p&gt;I withdraw approximately 40 percent of my complaint, maybe.&lt;/p&gt;

&lt;p&gt;But while I was reading about ReAct, the déjà vu didn't go away. There was something familiar about the loop, not the name, and eventually I realized why.&lt;/p&gt;

&lt;p&gt;The realization sent me back to &lt;a href="https://github.com/earlgreyhot1701D/verity-lex" rel="noopener noreferrer"&gt;Verity Lex&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's where the joke became considerably more embarrassing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apparently, I Had Already Met ReAct
&lt;/h2&gt;

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

&lt;p&gt;I built &lt;a href="https://verity-lex.vercel.app/" rel="noopener noreferrer"&gt;Verity Lex&lt;/a&gt; during &lt;a href="https://openai.devpost.com/" rel="noopener noreferrer"&gt;OpenAI Build Week&lt;/a&gt; because I wanted to turn the usual government technology sales question around: before asking what AI a court should adopt, what if we asked whether the institution was ready for AI in the first place?&lt;/p&gt;

&lt;p&gt;I've &lt;a href="https://dev.to/earlgreyhot1701d/open-ai-build-week-3fi8"&gt;written about that build and the question behind it before&lt;/a&gt;, so I won't retell that story here. What matters for this one is how Verity Lex goes looking for the evidence.&lt;/p&gt;

&lt;p&gt;Court websites aren't neat databases waiting for my application to query the right row. A policy might be a PDF. It might be a webpage. It might be buried under administration or linked from a page whose title tells you almost nothing. One document might reveal language that changes what should be searched for next.&lt;/p&gt;

&lt;p&gt;So I needed the model to look at what it found and decide what to do next. It searches, observes what comes back, reasons about what that result means, chooses another action, and continues until it has enough evidence or reaches the boundaries I gave it.&lt;/p&gt;

&lt;p&gt;If that sounds familiar, congratulations. You apparently knew what ReAct was before I did.&lt;/p&gt;

&lt;p&gt;The architecture in my own &lt;a href="https://github.com/earlgreyhot1701D/verity-lex" rel="noopener noreferrer"&gt;Verity Lex repository&lt;/a&gt; describes the controller as a model-directed ReAct loop:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;reason → act → observe → repeat&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There it was.&lt;/p&gt;

&lt;p&gt;Not metaphorically. Not "well, if you squint, this is sort of agentic."&lt;/p&gt;

&lt;p&gt;I had built it.&lt;/p&gt;

&lt;p&gt;More precisely, I had designed an application whose discovery architecture used it, worked with AI coding tools to implement it, documented the architecture, tested the application and deployed it.&lt;/p&gt;

&lt;p&gt;I just didn't understand that this behavior belonged to a formal pattern with a name and a research lineage.&lt;/p&gt;

&lt;p&gt;And because Verity Lex is itself a Next.js application, there was an additional indignity waiting for me: I had managed to put &lt;strong&gt;React and ReAct in the same project&lt;/strong&gt; before I understood that one had been helping render my interface while the other was helping determine what my agent should do next.&lt;/p&gt;

&lt;p&gt;Apparently I had already spoken to the manager.&lt;/p&gt;

&lt;p&gt;She was... &lt;em&gt;moi&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Do You Build Something You Don't Know?
&lt;/h2&gt;

&lt;p&gt;This is where I felt sheepish.&lt;/p&gt;

&lt;p&gt;There is an important distinction here, because I don't want to rewrite my own history into a story where I asked an AI to "make me an agent" and went to lunch while it assembled something I couldn't describe.&lt;/p&gt;

&lt;p&gt;Verity Lex had a PRD. It had architecture. I made deliberate decisions about what the model could control and, more importantly, what it could not.&lt;/p&gt;

&lt;p&gt;The model could direct discovery because adaptability was at the forefront of the build. It could not determine the final readiness score because reproducibility was the backbone of the analysis.&lt;/p&gt;

&lt;p&gt;I understood the behavior I wanted.&lt;/p&gt;

&lt;p&gt;What I didn't have was the formal vocabulary and lineage for part of the architecture I had specified. That distinction matters to me, although I'm still deciding how much comfort I should take from it.&lt;/p&gt;

&lt;p&gt;My path into software has happened at a peculiar moment. I didn't arrive through a computer science or engineering program where someone handed me foundational papers in chronological order. I arrived through questions, hackathons and AI coding assistants. Claude, ChatGPT and Gemini became part of how I could turn an idea into something executable long before I had enough technical knowledge to know the name of every pattern appearing underneath it.&lt;/p&gt;

&lt;p&gt;Apparently, somewhere along the way, I crossed through an academic idea without realizing I had entered the neighborhood.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ahem. I'm looking at you, &lt;a href="https://arxiv.org/abs/1706.03762" rel="noopener noreferrer"&gt;Attention Is All You Need&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At least that one became famous enough that even a new AI-assisted developer eventually hears about it.&lt;/p&gt;

&lt;p&gt;ReAct and I had apparently been working together without being formally introduced.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Chicken, the Egg, and My Software Education
&lt;/h2&gt;

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

&lt;p&gt;Before the AWS coursework, my mental model of an AI agent was mostly behavioral: agents go out and do things.&lt;/p&gt;

&lt;p&gt;Useful, but incomplete.&lt;/p&gt;

&lt;p&gt;Encountering ReAct through formal study gave me a way to look inside the "doing."&lt;/p&gt;

&lt;p&gt;The original &lt;a href="https://react-lm.github.io/" rel="noopener noreferrer"&gt;ReAct work&lt;/a&gt; describes reasoning traces and task-specific actions operating in an interleaved way: reasoning helps the model develop and update its plan, while actions let it interact with external sources and gather new information.&lt;/p&gt;

&lt;p&gt;Suddenly I had better language for what I had already asked Verity Lex to do.&lt;/p&gt;

&lt;p&gt;The model wasn't simply being released onto a court website with a tiny briefcase and an assignment. Its actions were part of a loop. The available tools constrained what it could do. The controller constrained how long it could continue. Observations changed what happened next. And my architecture drew a hard line between the part where model judgment was useful—discovery—and the part where I wanted deterministic behavior—scoring.&lt;/p&gt;

&lt;p&gt;The code hadn't changed.&lt;/p&gt;

&lt;p&gt;My understanding of my own code had.&lt;/p&gt;

&lt;p&gt;And this is where my embarrassment runs into the chicken-and-egg problem.&lt;/p&gt;

&lt;p&gt;I don't think that understanding would have happened, at least not now, without formal learning.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://aws.amazon.com/about-aws/our-impact/scholars/" rel="noopener noreferrer"&gt;AWS AI &amp;amp; ML Scholars program&lt;/a&gt; is explicitly designed to move learners from foundational AI education into deeper technical study. In 2026, AWS sponsored 100,000 learners in the Challenge phase, with the top 4,500 advancing to fully funded Udacity Nanodegrees. The Agent Developer track goes directly into reasoning patterns, RAG, tools, memory and APIs.&lt;/p&gt;

&lt;p&gt;I am pursuing that formal education because I know there are gaps between what I have learned to build and what I can fully explain.&lt;/p&gt;

&lt;p&gt;But without AI-assisted building, I don't know that I would have pursued this much software engineering knowledge in the first place.&lt;/p&gt;

&lt;p&gt;That's the part I keep circling.&lt;/p&gt;

&lt;p&gt;I didn't study ReAct, become fascinated by agent architecture, and then decide to build Verity Lex as an implementation exercise. I had questions. AI gave me enough leverage to turn some of those questions into software experiments, and building those experiments exposed the edges of what I understood.&lt;/p&gt;

&lt;p&gt;Those jagged edges and sharp cliffs made me want to learn more.&lt;/p&gt;

&lt;p&gt;That pursuit eventually put me in an Agentic Engineer Nanodegree staring at the word &lt;strong&gt;ReAct&lt;/strong&gt; while realizing that part of the lesson was already sitting in a repository I had shipped.&lt;/p&gt;

&lt;p&gt;Which one came first?&lt;/p&gt;

&lt;p&gt;The building or the learning?&lt;/p&gt;

&lt;p&gt;I'm increasingly not sure that's the right question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Ahead of Understanding
&lt;/h2&gt;

&lt;p&gt;I do feel silly about this, and I think that's a reasonable response.&lt;/p&gt;

&lt;p&gt;There are consequences to deploying software you cannot explain, and "AI helped me build it" is not a waiver from understanding security, architecture, cost, reliability or what your application is actually doing. Being able to ship something and understanding what you've shipped are different accomplishments.&lt;/p&gt;

&lt;p&gt;AI-assisted development can make the distance between those accomplishments deceptively easy to cross.&lt;/p&gt;

&lt;p&gt;I've crossed it.&lt;/p&gt;

&lt;p&gt;But my experience with ReAct has made me wonder whether the gap only tells one story.&lt;/p&gt;

&lt;p&gt;Without the AWS Scholars program and my decision to pursue formal learning alongside building, I might never have recognized this particular gap at all. I could have continued describing Verity Lex accurately at the product level while never acquiring the vocabulary that allowed me to examine part of its architecture more precisely.&lt;/p&gt;

&lt;p&gt;And without building Verity Lex first, ReAct might have arrived as another diagram in another lesson: reason, act, observe, repeat. Memorize it. Pass the quiz. Move along.&lt;/p&gt;

&lt;p&gt;Instead, I had déjà vu.&lt;/p&gt;

&lt;p&gt;I had a repository and architectural decisions I could revisit. I could ask why the model directs discovery but not scoring. I could look at the iteration limit and understand it as a boundary on agent autonomy rather than merely a number in a controller. I could see discovery, fetching and extraction not simply as functions, but as available actions inside a reasoning-and-acting loop.&lt;/p&gt;

&lt;p&gt;Formal learning gave names and structure to things experience had already put in my hands.&lt;/p&gt;

&lt;p&gt;Experience gave the formal learning somewhere to land.&lt;/p&gt;

&lt;p&gt;Neither seems complete without the other.&lt;/p&gt;

&lt;p&gt;That is a much less tidy education than the one I had imagined formal learning was supposed to provide.&lt;/p&gt;

&lt;p&gt;Learn the concept. Practice it. Build something.&lt;/p&gt;

&lt;p&gt;Mine sometimes runs forward, backward and sideways.&lt;/p&gt;

&lt;p&gt;I ask a question. I build something. I discover what I built. I realize what I don't understand. I go study. I return to the code and see something different. Then, if things are going well, I leave with a better question than the one that got me there.&lt;/p&gt;

&lt;p&gt;I don't know yet whether AI-assisted development has created a &lt;em&gt;good&lt;/em&gt; learning model here.&lt;/p&gt;

&lt;p&gt;I do know it has created a possible one, because I'm sprinting alongside it.&lt;/p&gt;

&lt;p&gt;For now, I understand considerably more about Verity Lex than I did when I shipped it. I have better language for why the model-directed loop works the way it does, what family of ideas it belongs to, and where its autonomy begins and deliberately ends.&lt;/p&gt;

&lt;p&gt;And I finally know the difference between React and ReAct.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt; is still on the frontend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://react-lm.github.io/" rel="noopener noreferrer"&gt;ReAct&lt;/a&gt; is helping me understand what happens when an agent has to decide what to do next.&lt;/p&gt;

&lt;p&gt;To Shunyu Yao, Jeffrey Zhao, Dian Yu, Nan Du, Izhak Shafran, Karthik Narasimhan and Yuan Cao: I have now read your work. The name makes sense.&lt;/p&gt;

&lt;p&gt;I apologize for questioning your judgment.&lt;/p&gt;

&lt;p&gt;Mostly.&lt;/p&gt;

&lt;p&gt;I'd still like to speak to the manager.&lt;/p&gt;

&lt;p&gt;AI assisted. Human approved. Powered by NLP. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>learning</category>
      <category>buildinpublic</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Sobremesa: Six meals in Mexico, heritage without an address.</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Fri, 07 Aug 2026 03:15:20 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/sobremesa-six-meals-in-mexico-heritage-without-an-address-24n3</link>
      <guid>https://dev.to/earlgreyhot1701d/sobremesa-six-meals-in-mexico-heritage-without-an-address-24n3</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, Perfect Landing&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Mexico is our heritage. Yet, we have no family there to visit.&lt;/p&gt;

&lt;p&gt;That sounds sadder than it is. What it actually meant, for the years before my wife and I were married and most of our time off since, is that we had to go find it ourselves. No family kitchen waiting. No grandmother's recipe with an address attached. Just the two of us and a country that is ours and that we did not know.&lt;/p&gt;

&lt;p&gt;So we did what every hungry person in a new city does...we ate.&lt;/p&gt;

&lt;p&gt;Six cities, six completely different cuisines, and somewhere in there it stopped feeling like traveling. A tlayuda from a stand outside Santo Domingo in Oaxaca. An hour in line at El Yaqui with a michelada in Rosarito. Different food every time. Same feeling every time, and there is no English word for that feeling.&lt;/p&gt;

&lt;p&gt;There is a Spanish one.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://sobremesa-dev.vercel.app/" rel="noopener noreferrer"&gt;Sobremesa&lt;/a&gt;&lt;/strong&gt; is the time you stay at the table after the food is gone, still talking. Not the meal. The part after the meal.&lt;/p&gt;

&lt;p&gt;That is the whole site. Six meals across six Mexican cities, and the thing it measures is not how good the food was. It is how long we stayed.&lt;/p&gt;

&lt;p&gt;Tijuana, one hour. Rosarito, two. Ensenada, one. Guadalajara, ninety minutes. Mexico City, two hours. Oaxaca, two. The page adds them up at the end. Nine hours and thirty minutes at six tables.&lt;/p&gt;

&lt;p&gt;Comfort food usually means a kitchen you can go back to. We do not have one over there. So the six tables became it. The stand at Plaza Santo Domingo is the family table. The hour in line at Tacos El Yaqui is the Sunday afternoon table.&lt;/p&gt;

&lt;p&gt;Each entry has the dish, where we ate it, one verified fact about the food, and one line that is just ours, from our experience. There is a form at the bottom where you add your own table and download a card of it, generated in your browser. Nothing gets sent anywhere.&lt;/p&gt;

&lt;p&gt;One static HTML file. No framework, no build step, no tracking, no cookies, no storage. Two fonts off Google Fonts and nothing else. Designed and built in Claude Design, with the research, the fact-checking and the accessibility spec done in Claude, and the illustrations generated in ChatGPT.&lt;/p&gt;

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

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

&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/earlgreyhot1701D/sobremesa" rel="noopener noreferrer"&gt;https://github.com/earlgreyhot1701D/sobremesa&lt;/a&gt; (MIT)&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/earlgreyhot1701D" rel="noopener noreferrer"&gt;
        earlgreyhot1701D
      &lt;/a&gt; / &lt;a href="https://github.com/earlgreyhot1701D/sobremesa" rel="noopener noreferrer"&gt;
        sobremesa
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      DEV.to Frontend Challenge entry, Comfort Food Edition. Sobremesa is Spanish for the time you stay at the table after the food is gone. Six meals, six Mexican cities, fourteen years, measured in minutes. Single static HTML page, zero dependencies, zero tracking, WCAG AA contrast computed rather than eyeballed.
    &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;Sobremesa&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/earlgreyhot1701D/sobremesa/assets/banner-readme.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%2Fearlgreyhot1701D%2Fsobremesa%2FHEAD%2Fassets%2Fbanner-readme.png" alt="Sobremesa banner — illustrated Mexican dishes on hand-painted talavera plates"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Six meals, six cities, one country.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Sobremesa&lt;/em&gt; is the Spanish word for the time you stay at the table after the food is gone, talking. There is no English equivalent.&lt;/p&gt;
&lt;p&gt;This is a record of six meals eaten in six Mexican cities over fourteen years, organized around one number: how long I stayed at each table.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://sobremesa-dev.vercel.app/" rel="nofollow noopener noreferrer"&gt;https://sobremesa-dev.vercel.app/&lt;/a&gt;
&lt;strong&gt;Write-up:&lt;/strong&gt; [add your dev.to post URL]&lt;/p&gt;
&lt;p&gt;Submitted to the &lt;a href="https://dev.to/challenges/frontend-2026-07-29" rel="nofollow"&gt;DEV Frontend Challenge, Comfort Food Edition&lt;/a&gt;, Perfect Landing prompt.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it is&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;A single static page. No framework, no build step, no dependencies, no tracking, no cookies, no storage.&lt;/p&gt;
&lt;p&gt;Six entries, each with a dish, a place, a verified fact, a personal note, and a duration. The durations are summed at runtime. At the end, a form lets you enter your own table and download a card of it, generated entirely in your browser.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Running it&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;There is nothing to install.&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&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/earlgreyhot1701D/sobremesa" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Try it with the keyboard. Tab through it, open a few tables with Enter or Space, and turn JavaScript off if you want to see the whole thing still read.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sobremesa-dev.vercel.app/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fakj844ydhbraz502g7h0.png" alt="Sobremesa: the hero, the word, its definition, and the six meals below it" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Planning and research handed me a dish I had never eaten
&lt;/h3&gt;

&lt;p&gt;San Miguel de Allende is one of my favorite places in the world.  But when I built this I realized it does not really have a signature dish. Most of what gets served there belongs to the wider Bajío. Research turned up fiambre sanmiguelense, a cold plate of pickled vegetables and shredded meat that shows up around Christmas and then vanishes for a year. It fit the slot perfectly.&lt;/p&gt;

&lt;p&gt;I have never eaten it.&lt;/p&gt;

&lt;p&gt;So instead I happily pivoted to Rosarito, along with Tacos El Yaqui where I have waited in line for an hour. I can tell you the line takes you past the wood fire where the carne is grilled and that they sell micheladas while you wait in it. Sobremesa started before the food did.&lt;/p&gt;

&lt;h3&gt;
  
  
  Placeholder data is sneaky
&lt;/h3&gt;

&lt;p&gt;Early on I dropped fake durations into the array so I could see the layout. One hour five. Two hours twenty. Forty-five minutes. They looked plausible, and they survived about six rounds of design work before I noticed that five of my six headline numbers were invented.&lt;/p&gt;

&lt;p&gt;Every duration on the live site is a real estimate now. Mexico City is two hours because we sat there for two hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  Half the good food facts turned out to be folklore
&lt;/h3&gt;

&lt;p&gt;I fact-checked every claim about every dish, and a lot of the best-sounding ones did not survive.&lt;/p&gt;

&lt;p&gt;The Baja fish taco was not invented by Japanese immigrants adapting tempura. There is no documented evidence chain, only the fact that Ensenada had Japanese fishing families in the early twentieth century. Adjacency is not a source.&lt;/p&gt;

&lt;p&gt;Birote salado, the bread a torta ahogada depends on, absolutely can be baked outside Guadalajara. Bakers abroad publish working formulas. The impossible-to-reproduce line is tourism copy that everyone repeats and nobody supports.&lt;/p&gt;

&lt;p&gt;And every accidental-discovery origin story is legend, not history. A vendor in Guadalajara drops a sandwich in salsa and invents the torta ahogada. A girl in Oaxaca over-curdles the milk and invents quesillo. Caesar Cardini runs out of ingredients in Tijuana and invents the Caesar salad. Three cities on this page, one folk template.&lt;/p&gt;

&lt;p&gt;The legends stayed off the page, which made the entries shorter and better. What is left is checkable. The beef for a Tijuana asada taco comes up from Ciudad Obregón, Sonora. El Huequito opened in 1959 and El Tizoncito in 1966, both claim tacos al pastor, and the page says plainly that neither claim is settled.&lt;/p&gt;

&lt;h3&gt;
  
  
  The prettiest thing on the page was quietly breaking it
&lt;/h3&gt;

&lt;p&gt;I had a paper grain texture over the whole page. Multiply blend, low opacity, the thing that makes flat cream look printed instead of rendered. It looked great.&lt;/p&gt;

&lt;p&gt;It was also multiplying my text. Not just the backgrounds. Every glyph, dragged down by a texture whose darkest pixels were pure black. Three color pairs fell below WCAG AA and nothing on screen looked wrong. You cannot see a 4.35 contrast ratio. You can only compute it.&lt;/p&gt;

&lt;p&gt;The fix was moving the grain from an overlay above the content to a per-surface background layer, so it multiplies against the background color only and never touches a glyph. The texture got regenerated with bounded alpha so the worst-case pixel is computable rather than pure black.&lt;/p&gt;

&lt;p&gt;Then every foreground and background pair was recomputed as composited under the grain. Twelve pairs, all passing, with the tightest one landing just above the 4.5 line. That pair has no headroom left, so it is written down in the README where future me will trip over it.&lt;/p&gt;

&lt;p&gt;The rest of the accessibility work is baked in. One &lt;code&gt;h1&lt;/code&gt;, no skipped levels. Real buttons, never a clickable div. &lt;code&gt;aria-expanded&lt;/code&gt; verified in the accessibility tree instead of trusted from the source. Focus that never gets lost. Every Spanish word wrapped in &lt;code&gt;lang="es"&lt;/code&gt; so a screen reader does not pronounce &lt;code&gt;micheladas&lt;/code&gt; with English phonics, which matters a great deal on this particular page. Every entry, fact and note present in the HTML, with JavaScript collapsing the panels on load rather than rendering the content, so the whole thing reads with JS off.&lt;/p&gt;

&lt;h3&gt;
  
  
  About the illustrations
&lt;/h3&gt;

&lt;p&gt;The six dish illustrations are AI-generated, and the page says so in the footer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Illustrated from memory. Fourteen years, and we photographed almost none of it consistently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which is true. We were eating, not documenting. An illustration that announces itself as one is fine. A render pretending to be a photograph is not.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it was built
&lt;/h3&gt;

&lt;p&gt;Multi-LLM, split by what each one is good at, which is what works for me. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude&lt;/strong&gt; did the research and the architecture. Six cities of food history, fact-checked against sources, with a written record of every claim that got cut and why. It also wrote the requirements spec, which turned out to be the single highest-leverage document in the project. Numbered requirements, testable acceptance criteria, zero visual direction. Handing a builder "make it accessible" gets you nothing. Handing it "every disclosure has aria-expanded reflecting real state and aria-controls pointing at a real id, verified in the accessibility tree" gets you something you can check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Design&lt;/strong&gt; built the page. I fed it the requirements doc with no aesthetic direction at all, deliberately, because my own art direction had been the problem for two days straight. Left to solve the spec on its own it produced something better than what I had been pushing toward. It also caught things I would not have: the empty photo slots had &lt;code&gt;alt=""&lt;/code&gt; without &lt;code&gt;aria-hidden&lt;/code&gt; once the 404 fired, and it fixed that unprompted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChatGPT&lt;/strong&gt; generated the six dish illustrations. One shared style block, then the first accepted image attached as the reference for the other five, which is the trick that stops a set of six from drifting into six different styles.&lt;/p&gt;

&lt;p&gt;I direct. The agents generate. I validate and decide. Every fact on that page was verified against a source and every design call was made by a human (me!), including the ones where the human was wrong for a while.&lt;/p&gt;

&lt;p&gt;The code itself is small. All six entries render from one array. DOM gets built with &lt;code&gt;createElement&lt;/code&gt; and &lt;code&gt;textContent&lt;/code&gt;, never &lt;code&gt;innerHTML&lt;/code&gt;. Every color and spacing value comes from a token, so nothing is hardcoded in a component rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attribution:&lt;/strong&gt; &lt;code&gt;index.html&lt;/code&gt; is original to this submission and contains no third-party code. The repo also carries the Claude Design component it was authored in, under &lt;code&gt;/design&lt;/code&gt;, which is not deployed. Type is &lt;a href="https://fonts.google.com/specimen/Alfa+Slab+One" rel="noopener noreferrer"&gt;Alfa Slab One&lt;/a&gt;, &lt;a href="https://fonts.google.com/specimen/Crimson+Pro" rel="noopener noreferrer"&gt;Crimson Pro&lt;/a&gt; and &lt;a href="https://fonts.google.com/specimen/Oswald" rel="noopener noreferrer"&gt;Oswald&lt;/a&gt;, all SIL Open Font License. The six dish illustrations and the hero were generated for this project. MIT licensed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend is freedom
&lt;/h3&gt;

&lt;p&gt;Most of what I build has a backend behind it. Auth, data models, an API that can fail in twelve different ways, a schema I will regret in a month. This had none of that. One HTML file, no server, nothing to deploy but a folder.&lt;/p&gt;

&lt;p&gt;What that bought me was room to be wrong and keep iterating. I rewrote the visual direction twice and the cost was a git checkout. No migration to undo, no state to reason about, no data to corrupt. Being able to throw out a whole design at ten at night and start over is a luxury I do not usually get.&lt;/p&gt;

&lt;p&gt;I went in thinking of the no-backend part as a constraint. It was a break and allowed me to be more creative when I skew utilitarian.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would do differently
&lt;/h2&gt;

&lt;p&gt;The type scale is in pixels, so browser text-only zoom will not enlarge it. Page zoom works, so it is not a strict AA failure, but it is a real gap for anyone who sets a minimum font size. A rem conversion is first on the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that is not about code
&lt;/h2&gt;

&lt;p&gt;We are still going strong. There are now more than six meals I'd love to share: Valle de Guadalupe, Querétaro, Guanajuato y más, y más, y más!&lt;/p&gt;

&lt;p&gt;But these are the six where we sat down and did not want to get up, in a country that is ours and where we know almost no one, and the food is how we got there.&lt;/p&gt;

&lt;p&gt;Six out of many. Provecho.&lt;/p&gt;




&lt;p&gt;Quick context if you are new here: I came to code from the courtroom. Jury services to AI builder since July 2025, self-taught, learning in public. I direct, the agents generate, I validate and decide.&lt;/p&gt;

&lt;p&gt;AI Assisted. Human Approved. Powered by NLP.&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>claude</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Netlify's Hot AR Summer Challenge: I Wasn't Going to Enter. 33 Apps Later, I'm So Glad I Did</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Sun, 02 Aug 2026 19:18:23 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/netlifys-hot-ar-summer-i-wasnt-going-to-enter-33-apps-later-heres-the-bill-1d25</link>
      <guid>https://dev.to/earlgreyhot1701d/netlifys-hot-ar-summer-i-wasnt-going-to-enter-33-apps-later-heres-the-bill-1d25</guid>
      <description>&lt;p&gt;On Friday, July 31, I was at date night dinner with my wife in LA. We sat down at 8. The plan was simple: enjoy dinner, drive the hour home by 11, post my last app, close out the month.&lt;/p&gt;

&lt;p&gt;It was a five course tasting menu. At 9:15 we had finished exactly one course.&lt;/p&gt;

&lt;p&gt;I did the math at the table. Four courses to go, an hour of driving after that, and a deadline that did not care about either. So I picked up my phone in panic. Not my laptop, which was at home. My phone.&lt;/p&gt;

&lt;p&gt;I built the &lt;a href="//hot-ar-summer-attestation.netlify.app"&gt;last one&lt;/a&gt; right there at the table, sitting across from my wife, between courses. Claude for the build. Then I deployed it from the same phone through Netlify Agent Runners running Gemini. A confetti canvas, a terminal card, a certified stamp, and a deterministic serial number, because the closing piece had to prove its own work too. Last app of the month! &lt;/p&gt;

&lt;p&gt;It went live. On the first try. From a restaurant table.&lt;/p&gt;

&lt;p&gt;Then I went to submit it and found there was nowhere to put it. The window had closed. My stomach dropped somewhere around the second course.&lt;/p&gt;

&lt;p&gt;We left at 11:30 and got home at 12:30 in the morning. Thirty-three apps made the wall. The thirty-fourth exists, works, and was never counted.&lt;/p&gt;

&lt;p&gt;That is the ending, so I am going to start there and work backward.&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%2Fcbwsdi2zdq2eutcsih16.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%2Fcbwsdi2zdq2eutcsih16.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  I wasn't going to enter
&lt;/h2&gt;

&lt;p&gt;Netlify ran a building challenge called &lt;a href="https://hot-ar-summer.netlify.app/" rel="noopener noreferrer"&gt;Hot AR Summer&lt;/a&gt; through July. Build with Agent Runners, ship a small app, put it on the wall. Two apps a day allowed. I saw it and thought, that is a nice idea for people with more time than me.&lt;/p&gt;

&lt;p&gt;Then I built one. Then the next day I built another one. Then it stopped being a decision.&lt;/p&gt;

&lt;p&gt;By the end I had &lt;a href="https://hot-ar-summer.netlify.app/showcase/builder/la-shara-cordero" rel="noopener noreferrer"&gt;33 apps on my builder page&lt;/a&gt;, out of a showcase somewhere north of 200 total from everyone who played. A worry filing system with real Dewey Decimal call numbers. A diner wheel that decides dinner by how long you hold the button. A quiz that tells you which dead language you would think in. A goth subtype sorter I built for my wife. A basketball dashboard about Luka. A coin flip that shows its arithmetic.&lt;/p&gt;

&lt;p&gt;None of them are big so I could move fast and break things. Ha! One screen, one idea, one self-contained &lt;code&gt;index.html&lt;/code&gt;, one deploy.&lt;/p&gt;

&lt;p&gt;What I did not expect was that the constraint I picked for aesthetic reasons would turn out to be a budget decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The builds are deterministic, and that started as an accident but ended up being a cost decision
&lt;/h2&gt;

&lt;p&gt;Here is the rule I ran all month: &lt;strong&gt;no &lt;code&gt;Math.random&lt;/code&gt; in any selection logic.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every app decides its output from the input. Type the same worry into Dewey, get the same call number. Hold the dinner wheel for the same 1.8 seconds, get the same supper. Press the Honest Coin at the same millisecond, get the same face and the same hash to prove it. The apps hash their inputs, run a fixed rule table, and show you the derivation.&lt;/p&gt;

&lt;p&gt;I like this for philosophical reasons. I came to code from the courtroom, and in that world a confident wrong answer is worse than no answer. A receipt you can check beats a result you have to trust.&lt;/p&gt;

&lt;p&gt;But the reason it survived contact with a credit meter is plainer than that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A deterministic app calls a model zero times after it is built.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All of the AI cost is in construction. Once the thing is on Netlify it is a static file doing arithmetic in the visitor's browser. There is no inference at runtime. There is no API key. There is nothing to rate limit, nothing to cache, nothing that gets more expensive when more people show up.&lt;/p&gt;

&lt;p&gt;I did not fully appreciate what that meant until I opened the billing page.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bill: $44, and 73 percent of it never touched a user
&lt;/h2&gt;

&lt;p&gt;Across July I paid $44 in top-ups. My current billing period shows 3,990 credits consumed. Here is where every one of them went.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line item&lt;/th&gt;
&lt;th&gt;Credits&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AI inference (Agent Runners)&lt;/td&gt;
&lt;td&gt;2,899.6&lt;/td&gt;
&lt;td&gt;72.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production deploys (41)&lt;/td&gt;
&lt;td&gt;615&lt;/td&gt;
&lt;td&gt;15.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent Runners compute (47.41 GB-hrs)&lt;/td&gt;
&lt;td&gt;474.1&lt;/td&gt;
&lt;td&gt;11.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web requests (3,160)&lt;/td&gt;
&lt;td&gt;0.6&lt;/td&gt;
&lt;td&gt;0.02%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth (0.03 GB)&lt;/td&gt;
&lt;td&gt;0.7&lt;/td&gt;
&lt;td&gt;0.02%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That works out to about &lt;strong&gt;121 credits per app&lt;/strong&gt;, or roughly &lt;strong&gt;$1.33 an app&lt;/strong&gt; if you divide the top-ups by 33.&lt;/p&gt;

&lt;p&gt;Three quarters of my spend was the model reading and writing code. Another quarter was deploying and the machine time to do it. And then there is that fourth row, which I want to sit on for a second.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cheapest thing I did all month was actually serve the apps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3,160 web requests. Six tenths of one credit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sixty percent of a single credit, to serve every visit to every app I shipped, for a month. That is not a rounding error in my bill, it &lt;em&gt;is&lt;/em&gt; the rounding error. Serving the apps cost less than one twentieth of what a single deploy cost.&lt;/p&gt;

&lt;p&gt;Here is the counterfactual that made me put my coffee down. Suppose I had built these the usual way (i.e. me) a lot of "AI app" demos get built, with a model call on every visit. Some little inference to generate the flavor text instead of computing it.&lt;/p&gt;

&lt;p&gt;Using my own measured Gemini rate from this month (2,254.97 credits for 16M tokens) and a conservative small call of about 1,200 tokens per visit, those same 3,160 visits would have run roughly &lt;strong&gt;534 credits&lt;/strong&gt;. Against the 0.6 I actually paid.&lt;/p&gt;

&lt;p&gt;That is about &lt;strong&gt;890 times more expensive&lt;/strong&gt;, and it would have cost nearly as much as all 41 of my deploys combined. At my actual traffic. Which is small. That number does not stay still if anything ever catches on.&lt;/p&gt;

&lt;p&gt;The 0.6 credits is measured. The 534 is my estimate, using my own token pricing and an assumed call size, so argue with it if you want.&lt;/p&gt;

&lt;p&gt;Either way the difference is not really about my traffic, it is about what happens next. &lt;strong&gt;My apps cost the same to run whether ten people visit or ten thousand. An app that calls a model on every visit gets more expensive with every single visitor.&lt;/strong&gt; One is a flat line. The other climbs forever. I got the flat line for free by being stubborn about &lt;code&gt;Math.random&lt;/code&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%2Fh6arqfzn2bixyirm83ze.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%2Fh6arqfzn2bixyirm83ze.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemini did the work. The expensive model did not do more work, it just cost more.
&lt;/h2&gt;

&lt;p&gt;I ran two providers through Agent Runners this month, and the split is instructive.&lt;/p&gt;

&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;Tokens&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Effective rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Google (Gemini)&lt;/td&gt;
&lt;td&gt;16M&lt;/td&gt;
&lt;td&gt;$12.53&lt;/td&gt;
&lt;td&gt;~$0.78 per M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;td&gt;2.6M&lt;/td&gt;
&lt;td&gt;$3.76&lt;/td&gt;
&lt;td&gt;~$1.45 per M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;OpenAI cost me about &lt;strong&gt;1.8x per token&lt;/strong&gt; for the same category of work: generate a single-file HTML toy. It is not that it did a bad job. Scrivener, my DOS-terminal typing tutor, came out of Codex and it is one of the better-built things on the wall. It just is not 1.8x better at making a small deterministic app, and when you are shipping one a day that multiple compounds.&lt;/p&gt;

&lt;p&gt;So the rule I landed on mid-month, and wrote into my tracker so I would stop relitigating it: &lt;strong&gt;default to Gemini, sketch it out by hand first, one deploy, do not re-prompt a working app.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That last clause is the one that actually saved money. Costs are per run &lt;em&gt;and&lt;/em&gt; per deploy at 15 credits each. Every "hmm let me just tweak that" is a triple charge: new run, new tokens, new deploy. I finished the month at &lt;strong&gt;41 deploys for 33 apps&lt;/strong&gt;, which is 1.24 deploys per app. That number is my discipline in one statistic.&lt;/p&gt;

&lt;p&gt;One more thing buried in the token data that surprised me. Of the 18.6M tokens I burned, only &lt;strong&gt;323K were output&lt;/strong&gt;. That is &lt;strong&gt;1.7 percent&lt;/strong&gt;. Ninety-eight percent of what I paid for was the model &lt;em&gt;reading&lt;/em&gt;: my spec, the existing file, the context. The code it wrote was almost free.&lt;/p&gt;

&lt;p&gt;Note to self: If you want to spend less, write a tighter spec. That is the lever. Not a shorter app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three tools, three jobs, and none of them doing the other one's work
&lt;/h2&gt;

&lt;p&gt;The workflow that emerged, and it served me well:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude for brainstorm and architecture.&lt;/strong&gt; What is the idea, what is the rule table, what is the deterministic engine, what is the voice. This is where I argued about whether the app was any good before spending a credit on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChatGPT for the HTML.&lt;/strong&gt; Hand it a tight spec with the guardrails baked in (no &lt;code&gt;Math.random&lt;/code&gt;, &lt;code&gt;textContent&lt;/code&gt; not &lt;code&gt;innerHTML&lt;/code&gt;, no storage, no fetch, must fit one screen) and let it produce the file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt; Agent Runners for deploy.&lt;/strong&gt; One runner, one deploy, done.&lt;/p&gt;

&lt;p&gt;I want to be specific about this part, because it is the reason a daily streak was even possible for someone with a full time job. Deploying was not a project. There was no pipeline to babysit, no build config to fight, no ceremony. Point a runner at the file and it is live on a URL you can send to your wife thirty seconds later. Thirty-three times in a row, including one time from my phone at a restaurant table between courses.&lt;/p&gt;

&lt;p&gt;If you are new, this is the on-ramp I would hand you. If you are not new but you move fast and you resent tooling that slows you down, same recommendation for the opposite reason. The whole reason I shipped 33 small things instead of overthinking three big ones is that deploying cost me nothing emotionally. Fifteen credits and no stress (thank you &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;!).&lt;/p&gt;

&lt;p&gt;What worked for me during this sprint: I figured out the whole thing iterating with Claude before running anything, because thinking is free and inference is not. What I didn't take into account: any time I let a second pass happen because I did not specify well enough the first time.&lt;/p&gt;

&lt;p&gt;I have started sorting those second passes into a taxonomy, because they are not all the same failure. Agent-miss (it ignored the spec), spec-miss (I never said it), scope-change (I changed my mind), operator-error (I ran the wrong thing). Only one of those four is the model's fault, and it is not the most common one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The apps I would show you first
&lt;/h2&gt;

&lt;p&gt;These are the ones I would hand you first. All free, all instant, none of them want your email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://whats-for-dinner-tonight.netlify.app/" rel="noopener noreferrer"&gt;What's for Dinner?&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
A 1950s chrome diner wheel. Press and hold to wind it up, release, and it clatters to a stop on tonight's supper. The longer you hold, the further it spins, and the same hold always lands the same meal. There is a bell, an order ticket, and the button afterward says "Spin again (we both know why)." Cereal is one of the eight outcomes and I refuse to apologize for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://crypt-type.netlify.app/" rel="noopener noreferrer"&gt;Crypt Type&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Which goth subtype are you. Eleven results, each with a soundtrack and a receipt showing what you scored highest on. I built this one for my wife, who is the household authority on the subject and who tested it before anyone else got to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://saint-of-small-things.netlify.app/" rel="noopener noreferrer"&gt;Saint of Small Things&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Name something small you are struggling with and get canonized as its patron saint, feast day included. Patron saint of the unanswered email. It is silly and then it is somehow not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://vivant-linguae-mortuae.netlify.app/" rel="noopener noreferrer"&gt;Vivant Linguae Mortuae&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Eight questions, and it tells you which dead language you would think in. The most-shared one of the whole set, and the one people argue with me about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://mood-ring-index.netlify.app/" rel="noopener noreferrer"&gt;Mood Ring&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Press the stone and hold it. Your mood comes from how long you held, not from a dice roll, so if you sit with it you get a different answer than if you tap. Turns out that is the honest mechanic for a mood ring anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://how-long-ago.netlify.app/" rel="noopener noreferrer"&gt;How Long Ago Was That&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Type a year and it tells you the distance in something your body can feel. Type 1994 and learn it is closer to the moon landing than to today. This is the one Netlify demoed on their livestream, where it repeated itself on air and taught me something I had to write up separately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://same-sky.netlify.app/" rel="noopener noreferrer"&gt;Same Sky&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Where the sun is right now, wherever you are, from real solar geometry instead of a weather API. Pick Svalbard in July and it will tell you the sun does not set there today, which is correct and still feels like a bug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://with-appreaciation.netlify.app/" rel="noopener noreferrer"&gt;With Appreciation&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Name three things you are grateful for and receive a curated, properly sourced quote. It also tells you that others are here in quiet company, without a fake live counter, because I was not going to invent traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://luka-fit-index.netlify.app/" rel="noopener noreferrer"&gt;Luka Fit Index&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
A sports dashboard that shows you its own bias instead of hiding it. This was the first thing I shipped for Hot AR Summer, before I knew there would be thirty-two more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://messi-or-yamal.netlify.app/" rel="noopener noreferrer"&gt;Messi vs. Yamal&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://the-decision.netlify.app/" rel="noopener noreferrer"&gt;The Decision 2026&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Two generations meeting in a final, and LeBron's career treated as a unit of measurement. The second one is funnier than it has any right to be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://is-it-friday-hmmm.netlify.app/" rel="noopener noreferrer"&gt;Is it Friday?&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
The entire app is one word. That is the whole product and I stand by it.&lt;/p&gt;

&lt;p&gt;The full wall of all 33 is here: &lt;a href="https://hot-ar-summer.netlify.app/showcase/builder/la-shara-cordero" rel="noopener noreferrer"&gt;my Hot AR Summer builder page&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The last one shipped. It just never got counted.
&lt;/h2&gt;

&lt;p&gt;The final thing I made was not an app. It was a receipt for the month: &lt;a href="https://hot-ar-summer-attestation.netlify.app/" rel="noopener noreferrer"&gt;an attestation page&lt;/a&gt; with confetti, a terminal window, and a certified stamp. The serial on it is &lt;strong&gt;5307-062B&lt;/strong&gt;, and it is not decorative. It is FNV-1a run over the string &lt;code&gt;la-shara-cordero|hot-ar-summer|2026-07-31&lt;/code&gt;. Same name, same event, same date, same serial, forever. You can check my arithmetic.&lt;/p&gt;

&lt;p&gt;There is one asterisk on that page and I am going to point at it myself. The confetti uses &lt;code&gt;Math.random&lt;/code&gt;. I left a comment in the source saying so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Deterministic-ish spread without Math.random dependence for the pattern,&lt;/span&gt;
&lt;span class="c1"&gt;// but confetti is decorative so a little jitter is fine here.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the line I drew all month, and it took thirty-three apps to find it. The rule was never "randomness is bad." The rule was &lt;strong&gt;nothing you are asked to believe may be random.&lt;/strong&gt; The serial is a claim, so it is computed. The confetti is a feeling, so it can scatter. If I had made the serial random it would have been a sticker. If I had made the confetti deterministic it would have been a chore.&lt;/p&gt;

&lt;p&gt;Here is the context I am not going to dress up. That Friday I left the house at 6 in the morning and got home at 6:30 at night. Then we drove to LA for dinner. I was not running on a full tank when I sat down at that table, and I was still planning to ship something before midnight.&lt;/p&gt;

&lt;p&gt;I am not telling you that to flex. I am telling you because it is the actual reason the last one missed. I tried to overachieve on an empty day and it did not work. Sometimes pushing produces a good story. This time it produced a live URL nobody was allowed to count.&lt;/p&gt;

&lt;p&gt;Thirty-three apps counted. The thirty-fourth is real, shipped, and uncounted.&lt;/p&gt;

&lt;p&gt;It stung. It also might be the most accurate possible ending for a month spent learning that the deploy is a cost, the second pass is a cost, and time is the one budget with no top-up button. The work existing and the work being counted are two different things, and only one of them was ever up to me.&lt;/p&gt;

&lt;p&gt;Would I do it again? You know it! It was so much fun!!&lt;/p&gt;

&lt;p&gt;Coming up with the ideas was the best part. Sitting there working out what would actually make somebody smile, what is worth one screen, what has a joke in it and a real mechanism underneath. That is a harder puzzle than the code and I loved every round of it.&lt;/p&gt;

&lt;p&gt;The wins I am keeping:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leaning all the way into deterministic building.&lt;/strong&gt; I went in with it as a preference and came out with it as a practice. That is a real shift and it is mine now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning Netlify from scratch.&lt;/strong&gt; New tool, thirty-three deploys, zero drama. Add it to the kit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Getting demoed by a company for the first time.&lt;/strong&gt; Netlify put &lt;a href="//how-long-ago.netlify.app"&gt;How Long Ago Was That&lt;/a&gt; on their livestream. That had never happened to anything I built.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failing on that livestream, publicly, in real time.&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=2JlOsW43muk" rel="noopener noreferrer"&gt;Here is the moment&lt;/a&gt;. My &lt;a href="//how-long-ago.netlify.app"&gt;app repeated&lt;/a&gt; itself on air and I got the lesson handed to me in front of an audience. I would not trade it. You do not learn that one from a blog post, ugh!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And a huge thank you to &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt; for running this. A whole month of low-stakes, high-joy building with a wall to put things on. That is a gift to anyone learning, and I got a lot more out of it than 33 URLs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I am taking with me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Determinism is not a purity thing, it is a pricing model.&lt;/strong&gt; Building it into the app moves the entire cost to construction and gives you a flat line forever. That is worth more than the variety you give up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix for repetitive output in a deterministic tool is never randomness. It is more knowledge.&lt;/strong&gt; More anchors, more rules, a wider table. Randomness would make the output unrepeatable, and the receipt is the product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your spend is mostly input tokens.&lt;/strong&gt; Ninety-eight percent of mine was. Tighten the spec, not the app.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;One deploy.&lt;/strong&gt; Say it out loud before you press the button.&lt;/p&gt;

&lt;p&gt;I did not plan on doing this. I am so glad I did. Thirty-three small &lt;strong&gt;toys&lt;/strong&gt; exist now that did not exist on June 30, and the whole month cost me $44.&lt;/p&gt;

&lt;p&gt;Which, and I promise I did not plan this either, is less than the dinner.&lt;/p&gt;

&lt;p&gt;Quick context if you are new here: I came to code from the courtroom. Jury services to AI builder, starting July 2025 and still going, self-taught, learning in public. I direct, the agents generate, I validate and decide. I build the Clew Suite and a handful of civic tech tools, and you can see the rest of it at &lt;a href="https://earlgreyhot1701d.github.io/Clew-Labs/" rel="noopener noreferrer"&gt;Clew Labs&lt;/a&gt;. That is the lens I am writing from.&lt;/p&gt;

&lt;p&gt;AI Assisted. Human Approved. Powered by NLP.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ai</category>
      <category>webdev</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>AWS Amplify SSR gotcha: chasing a bug that was never in my code</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Sun, 02 Aug 2026 00:52:54 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/gotcha-chasing-a-bug-that-was-never-in-my-code-59cc</link>
      <guid>https://dev.to/earlgreyhot1701d/gotcha-chasing-a-bug-that-was-never-in-my-code-59cc</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The build was done. &lt;a href="https://themislex.org/" rel="noopener noreferrer"&gt;Themis Lex&lt;/a&gt; worked on my machine, and not in the "works if you squint" way. A court clerk enters their role, describes their workflow, picks a data sensitivity level, and gets back a PDF with two sections: where AI can safely support the work, and where it must never touch it. Claude via Bedrock generates the assessment. Server-side PDF render. No accounts, no storage, session ends when the download does.&lt;/p&gt;

&lt;p&gt;Three weeks solo, for the Women in &lt;a href="https://buildclub.ai/" rel="noopener noreferrer"&gt;AI Accelerator Spring 2026 Build Challenge.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Initial commit went in at 7:06pm on May 9. I pushed to &lt;a href="https://aws.amazon.com/amplify/?trk=1fdaebb5-a9f5-49f3-9c12-f587a2d4dcfc&amp;amp;sc_channel=ps&amp;amp;ef_id=CjwKCAjwsrbTBhAvEiwA0Bpp4Ys9dAS8MBZRSqcwC8_drDJev5q6sSFS_-uazIoROLPmT6xwpqi7ohoC6tgQAvD_BwE&amp;amp;gads_camp=23527793912&amp;amp;gads_ag=187898876850&amp;amp;gads_ad=795794010895&amp;amp;gads_kw=aws%20amplify&amp;amp;gads_matchtype=e&amp;amp;gads_network=g&amp;amp;gads_device=c&amp;amp;gads_geo=9031614&amp;amp;gad_campaignid=23527793912&amp;amp;gbraid=0AAAAADjHtp8X61wtlFNzZZjRnRi1-GDW5&amp;amp;gclid=CjwKCAjwsrbTBhAvEiwA0Bpp4Ys9dAS8MBZRSqcwC8_drDJev5q6sSFS_-uazIoROLPmT6xwpqi7ohoC6tgQAvD_BwE" rel="noopener noreferrer"&gt;AWS Amplify&lt;/a&gt;. Build went green. I opened the live site, filled out the form, hit submit.&lt;/p&gt;

&lt;p&gt;Nothing.&lt;/p&gt;

&lt;p&gt;Twenty eight seconds later, "Request timed out."&lt;/p&gt;

&lt;p&gt;I told myself the bug was not in my code. Everything ran locally. This had to be a platform problem. That belief carried me all night. It mostly held up. The exception was the first thing I should have checked.&lt;/p&gt;

&lt;p&gt;Here is the commit log, because it tells the story better than I can:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;19:06  Initial commit: Themis Lex MVP
20:31  refactor: migrate Bedrock auth to IAM compute role
22:29  diag: log credential env vars at runtime (booleans only, remove after fix)
22:40  fix: forward BEDROCK_MODEL_ID to SSR runtime via next.config.js env
22:50  fix: switch to InvokeModelWithResponseStreamCommand to beat 28s Lambda timeout
  ...
06:28  fix: remove unused type export that broke isolatedModules build
06:37  fix: end-to-end response streaming to beat Amplify 28s gateway timeout
06:48  fix: reduce max_tokens to 3000 to fit Amplify 30s timeout
06:52  fix: reduce max_tokens to 2000, 3000 still exceeded 30s timeout
07:00  fix: switch to Claude Haiku 4.5 to fit Amplify 30s timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ten and a half hours from first deploy to the fix that shipped it. That gap between 22:50 and 06:28 is me sleeping on it, which turned out to be the second most productive thing I did. Ha!&lt;/p&gt;

&lt;h2&gt;
  
  
  The error message was the absence of an error message
&lt;/h2&gt;

&lt;p&gt;My first instinct was that my code threw a fit and I swallowed it. I had a try/catch on the Bedrock call and it logged nothing. No stack trace. No AccessDenied. No throttle. My catch block was never entered, because my code never reached the part that could fail.&lt;/p&gt;

&lt;p&gt;That is a specific kind of awful. A loud error points at a line. Silence points at everything.&lt;/p&gt;

&lt;p&gt;The AWS SDK was stuck upstream of my logic, walking the credential provider chain looking for something to sign with, finding nothing, and waiting until the gateway killed it. From the outside that looks like a slow API. From the inside it is a permissions problem wearing a timeout costume.&lt;/p&gt;

&lt;p&gt;So here is the correction to my own title, four paragraphs in. One of the bugs was absolutely in my code.&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;// c136357, initial commit. This disables the provider chain.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;BedrockRuntimeClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BedrockRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_REGION&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 792b94f, "migrate Bedrock auth to IAM compute role"&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;BedrockRuntimeClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BedrockRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_REGION&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passing &lt;code&gt;credentials&lt;/code&gt; at all tells the SDK you have this handled, so it stops looking. Empty strings are still a value, so it tried to sign with nothing. Omit the object entirely and the default chain covers both local dev reading &lt;code&gt;.env.local&lt;/code&gt; and production reading the Lambda's assumed role. Two lines deleted. That was mine.&lt;/p&gt;

&lt;p&gt;At 22:29 I committed a diagnostic that logged credential env vars as booleans only, never values, because I could not tell from the outside whether the runtime had credentials at all. If you are stuck in the same silence, that is the move. Do not log the values. Log whether they exist.&lt;/p&gt;

&lt;p&gt;One thing, since this challenge is powered by Sentry: I had no error tracking on this app at all. It also would not have mattered in the usual way, because there was no exception to capture. Nothing threw. The request stopped existing when the gateway hung up, and application level error handling has nothing to report about a request that never failed so much as ended. What I needed was not a stack trace. It was something watching from outside the Lambda that could tell me requests were dying at a suspiciously round twenty eight seconds, which is the pattern I only saw hours later by reading CloudWatch REPORT lines by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  I changed two things at once, so I still cannot tell you which one worked
&lt;/h2&gt;

&lt;p&gt;I also changed the compute role's trust policy to include both &lt;code&gt;amplify.amazonaws.com&lt;/code&gt; and &lt;code&gt;lambda.amazonaws.com&lt;/code&gt;, and attached the role at the branch level instead of relying on the app level default. Redeployed. The credential hang was gone.&lt;/p&gt;

&lt;p&gt;Classic mistake. Two changes, one test, no isolated variable.&lt;/p&gt;

&lt;p&gt;When I went back to check my work for this post, the official docs contradict both of my theories. The &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/amplify-SSR-compute-role.html" rel="noopener noreferrer"&gt;AWS compute role page&lt;/a&gt; shows a single trust principal, &lt;code&gt;amplify.amazonaws.com&lt;/code&gt;, and says an app level role applies to all branches by default. &lt;a href="https://www.uncommonengineer.com/docs/engineer/AWS/amplify-ssr-iam-debugging/" rel="noopener noreferrer"&gt;Another engineer tested the two principal theory and rejected it&lt;/a&gt;, finding instead that roles created by CLI behaved differently from roles created in the console.&lt;/p&gt;

&lt;p&gt;I made both IAM changes at the same time as deleting the &lt;code&gt;credentials&lt;/code&gt; object, the hang stopped, and I cannot prove which one did it. It may well have been the two deleted lines all along. Adding the second principal is harmless. Branch level attachment is recommended anyway. But if you came here for a confirmed root cause on the IAM half, I do not have one, and I would rather say that than hand you a confident answer I cannot back up.&lt;/p&gt;

&lt;p&gt;What I can hand you is the diagnostic, which beats my guess:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws amplify get-app &lt;span class="nt"&gt;--app-id&lt;/span&gt; YOUR_APP_ID
aws amplify get-branch &lt;span class="nt"&gt;--app-id&lt;/span&gt; YOUR_APP_ID &lt;span class="nt"&gt;--branch-name&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare &lt;code&gt;computeRoleArn&lt;/code&gt; on both. And treat the IAM Console's "Last activity" on the role with suspicion, because the build service assuming the role lights it up whether or not your runtime ever gets credentials. It told me the role was in use. The role was in use. Just not by the thing that needed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The docs told me half of it. I was reading the wrong half.
&lt;/h2&gt;

&lt;p&gt;Credentials working. Next failure, eleven minutes later: &lt;code&gt;BEDROCK_MODEL_ID&lt;/code&gt; came back &lt;code&gt;undefined&lt;/code&gt; in production. Set in Amplify Console. Visible during build. Gone at runtime.&lt;/p&gt;

&lt;p&gt;This one is documented, and it is my miss, not AWS hiding the ball. The page is &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/ssr-environment-variables.html" rel="noopener noreferrer"&gt;Making environment variables accessible to server-side runtimes&lt;/a&gt;, and it says the behavior is intentional. Amplify env vars flow into the build environment. They are not injected into the SSR Lambda. Vercel, Railway, and Render do both, which is why the assumption never got questioned.&lt;/p&gt;

&lt;p&gt;AWS's sanctioned fix is a build spec line writing values into &lt;code&gt;.env.production&lt;/code&gt; before the build. My &lt;code&gt;amplify.yml&lt;/code&gt; does no such thing. It runs &lt;code&gt;npm ci&lt;/code&gt; and &lt;code&gt;npm run build&lt;/code&gt; and that is it. What I did instead, at 22:40, was inline the value through &lt;code&gt;next.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;BEDROCK_MODEL_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BEDROCK_MODEL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mine works. Theirs is the documented path. Use theirs if you want to point at a doc when a teammate asks.&lt;/p&gt;

&lt;p&gt;The nastier version of this bug is the silent one, and I shipped it. &lt;code&gt;lib/bedrock.ts&lt;/code&gt; looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modelId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BEDROCK_MODEL_ID&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us.anthropic.claude-haiku-4-5-20251001-v1:0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that env var ever failed to reach the runtime again, nothing throws. The app quietly runs the hardcoded fallback, and I find out when someone tells me the output changed. I wrote a warning about this trap and then shipped the trap.&lt;/p&gt;

&lt;p&gt;I fixed it while writing this post. The build now fails if &lt;code&gt;BEDROCK_MODEL_ID&lt;/code&gt; is missing, the runtime throws a named config error instead of falling back, and the API route returns a message the browser can show instead of a blank screen. Writing it down is what made me go look at it.&lt;/p&gt;

&lt;p&gt;Then Bedrock threw &lt;code&gt;AccessDeniedException&lt;/code&gt; complaining about AWS Marketplace actions I did not think I was using. Since the &lt;a href="https://aws.amazon.com/blogs/security/simplified-amazon-bedrock-model-access/" rel="noopener noreferrer"&gt;October 2025 model access change&lt;/a&gt;, Bedrock auto enables models on first call, the enablement runs through Marketplace, and the calling principal needs permission for it. The old Console "Model access" page is retired.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"aws-marketplace:ViewSubscriptions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"aws-marketplace:Subscribe"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Resource: "*"&lt;/code&gt; because Marketplace subscriptions are not scoped to model ARNs. Documented, in a Bedrock security blog post, with no link to it from anywhere in the Amplify docs. To find it you have to already suspect Marketplace, which I did not, because I was configuring Bedrock.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wall that is not written down anywhere
&lt;/h2&gt;

&lt;p&gt;Credentials working. Permissions working. Env vars working. My API route still died at twenty eight seconds.&lt;/p&gt;

&lt;p&gt;Amplify Hosting SSR has a hard gateway timeout in the twenty eight to thirty second range, and there is no setting AWS exposes to raise it.&lt;/p&gt;

&lt;p&gt;I looked for this. It is not in &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/quotas-chapter.html" rel="noopener noreferrer"&gt;Amplify quotas&lt;/a&gt;, which covers app counts and artifact sizes. It is not in &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/troubleshooting-SSR.html" rel="noopener noreferrer"&gt;troubleshooting SSR&lt;/a&gt;, which discusses 504s only in terms of a response size cap. As far as I can find, the number appears in no AWS documentation page at all.&lt;/p&gt;

&lt;p&gt;Where it does appear is &lt;a href="https://github.com/aws-amplify/amplify-hosting/issues/3223" rel="noopener noreferrer"&gt;aws-amplify/amplify-hosting issue 3223&lt;/a&gt;, still open, where an AWS engineer states that increasing the timeout on SSR compute is not supported at this time. A companion issue at &lt;a href="https://github.com/aws-amplify/amplify-hosting/issues/3475" rel="noopener noreferrer"&gt;3475&lt;/a&gt; gets redirected back to it.&lt;/p&gt;

&lt;p&gt;This is the part of the night I could not have prevented by reading. It is a platform constraint that lives in a GitHub thread.&lt;/p&gt;

&lt;p&gt;On the numbers, I want to be precise about what I know. My CloudWatch REPORT lines showed &lt;code&gt;Duration: 28006ms&lt;/code&gt;, which is the Lambda being killed, not the generation finishing. My planning notes from block 4 testing put Sonnet 4.6 at roughly forty two seconds at &lt;code&gt;max_tokens=6000&lt;/code&gt;. That is an observation from testing, not instrumented measurement, because the call never completed inside the Lambda's life. I never got a clean number. I got a wall.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streaming was the right idea, twice, and the wrong fix both times
&lt;/h2&gt;

&lt;p&gt;Streaming looked like the answer. Keep bytes flowing, keep the connection alive. I tried it in two separate shapes, seven hours apart.&lt;/p&gt;

&lt;p&gt;At 22:50 I switched to &lt;code&gt;InvokeModelWithResponseStreamCommand&lt;/code&gt; inside &lt;code&gt;lib/bedrock.ts&lt;/code&gt;, collecting chunks server-side. The route handler did not change. It still returned one JSON payload at the end. That keeps the Lambda demonstrably alive during generation, which is something, but it does nothing for a gateway measuring wall clock time on the response.&lt;/p&gt;

&lt;p&gt;Then I slept.&lt;/p&gt;

&lt;p&gt;At 06:37, after a build break at 06:28 over an unused type export, I went end to end. &lt;code&gt;streamBedrock()&lt;/code&gt; became an async generator. &lt;code&gt;pages/api/assess.ts&lt;/code&gt; set &lt;code&gt;Content-Type: text/plain&lt;/code&gt; and &lt;code&gt;Transfer-Encoding: chunked&lt;/code&gt; and called &lt;code&gt;res.write()&lt;/code&gt; per chunk. The client used &lt;code&gt;response.body.getReader()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Still dead at twenty eight seconds.&lt;/p&gt;

&lt;p&gt;CloudWatch showed the Lambda receiving Bedrock chunks the whole time. The client saw the platform timeout. Same symptom as the credential bug, completely different cause. I spent the first twenty minutes of it re-checking IAM.&lt;/p&gt;

&lt;p&gt;Next.js Pages API routes buffer. My writes did not reach the gateway as they were written. They queued until &lt;code&gt;res.end()&lt;/code&gt;, and by then the connection was gone. I reverted the whole end to end attempt eleven minutes later.&lt;/p&gt;

&lt;p&gt;AWS does document this, sort of. &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/ssr-amplify-support.html" rel="noopener noreferrer"&gt;Amplify support for Next.js&lt;/a&gt; lists "Next.js streaming" flatly under unsupported features. One line, no detail. I read that page. I read it as a caveat rather than a wall, which is a reading comprehension problem I have been thinking about since.&lt;/p&gt;

&lt;p&gt;The server-side collection version survived, and it is what runs today. The client still gets one JSON response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going smaller did not work. Going faster did.
&lt;/h2&gt;

&lt;p&gt;After the revert, I did the predictable thing and made the response smaller.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;max_tokens&lt;/code&gt; 6000 to 3000 at 06:48. Still over.&lt;br&gt;
3000 to 2000 at 06:52. Still over.&lt;/p&gt;

&lt;p&gt;Four minutes apart, watching the same wall. At that point the output was small enough to hurt the product and still too slow to ship.&lt;/p&gt;

&lt;p&gt;So at 07:00 I stopped shrinking the response and changed the model. Claude Sonnet 4.6 to Claude Haiku 4.5.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; nothing rendered. Twenty eight second hang, platform error page, &lt;code&gt;max_tokens&lt;/code&gt; cut to a third of the design target and still failing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt; PDF generated and downloaded. The test call that finally returned HTTP 200 showed 14.3 seconds in my terminal. That number is not in any log file or benchmark in the repo, so take it as what it is, one observed run during a live test at seven in the morning. What I can point to is the outcome: it fit, and it fit with enough room that I raised &lt;code&gt;max_tokens&lt;/code&gt; back up to 4000, above where I had cut it and closer to what the assessment needs.&lt;/p&gt;

&lt;p&gt;Themis Lex went live at &lt;a href="https://themislex.org" rel="noopener noreferrer"&gt;themislex.org&lt;/a&gt; running Haiku.&lt;/p&gt;

&lt;p&gt;There is a trade here and I am not going to pretend otherwise. Haiku's assessments are less nuanced than Sonnet's were. For a tool telling court staff where AI must never touch their work, the nuance is the thing they are paying attention to. I logged the quality delta as a v2 item rather than calling it a win.&lt;/p&gt;

&lt;p&gt;The other paths out, if you cannot shrink or speed up enough:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda Function URL with response streaming enabled. Fifteen minute timeout, server sent streaming that works.&lt;/li&gt;
&lt;li&gt;A separate Lambda behind API Gateway. Sixty second timeout.&lt;/li&gt;
&lt;li&gt;Move hosting. Vercel Pro gives sixty seconds and first class Next.js streaming.
None of those are an overnight fix. They are architecture decisions, which means they belong in the plan before you build, not in the panic after you deploy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I would do differently
&lt;/h2&gt;

&lt;p&gt;About half that night was avoidable and half was not, and the split is the lesson.&lt;/p&gt;

&lt;p&gt;Avoidable: the env var behavior and the streaming limitation are both written down. I did not read the right pages, and once I did, both took minutes. If you are deploying Next.js SSR to Amplify, read &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/ssr-environment-variables.html" rel="noopener noreferrer"&gt;ssr-environment-variables.html&lt;/a&gt; first. It states the runtime gap outright and links onward to compute roles, which is the on ramp to the whole IAM half of my night.&lt;/p&gt;

&lt;p&gt;Also avoidable: the credentials object. That was two lines of my own code buying me hours of platform suspicion.&lt;/p&gt;

&lt;p&gt;Not avoidable: the twenty eight second wall. No doc, no quota page, no warning. A GitHub issue and an AWS engineer saying no.&lt;/p&gt;

&lt;p&gt;The question I did not ask before choosing a platform, and will ask every time now: &lt;strong&gt;will any route in this app take longer than twenty five seconds end to end?&lt;/strong&gt; LLM calls, PDF renders, large file work, slow third party APIs. If yes, Amplify SSR is the wrong default and no amount of configuration changes that.&lt;/p&gt;

&lt;p&gt;If no, Amplify is fine. It is a good product for fast routes and content sites and normal CRUD, and the developer experience holds up. I do not mind that the constraint exists. I mind that I found it at 6:52 in the morning, cutting &lt;code&gt;max_tokens&lt;/code&gt; for the second time, instead of on a docs page in week one.&lt;/p&gt;

&lt;p&gt;I still use Amplify. I stopped reaching for it first, and I now run a fifteen minute timing spike on the slowest route before committing to any host. Pass or fail, before I build on it.&lt;/p&gt;

&lt;p&gt;Themis Lex shipped. It runs a smaller model than I designed it around, and I would rather tell you that than let you think the night went clean.&lt;/p&gt;




&lt;p&gt;Quick context if you are new here: I came to code from the courtroom. Jury services to AI builder in about a years, self-taught, learning in public. I direct, the agents generate, I validate and decide. I build the Clew Suite and a handful of civic tech tools. That is the lens I am writing from.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/earlgreyhot1701D/themis-lex" rel="noopener noreferrer"&gt;github.com/earlgreyhot1701D/themis-lex&lt;/a&gt;&lt;br&gt;
Live: &lt;a href="https://themislex.org" rel="noopener noreferrer"&gt;themislex.org&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI Assisted. Human Approved. Powered by NLP.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>aws</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Lemonade Second Squeeze: Model Archeology on 2019's GPT-2XL</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Mon, 27 Jul 2026 01:24:28 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/lemonade-second-squeeze-model-archeology-on-2019s-gpt-2xl-32jm</link>
      <guid>https://dev.to/earlgreyhot1701d/lemonade-second-squeeze-model-archeology-on-2019s-gpt-2xl-32jm</guid>
      <description>&lt;p&gt;Two weeks ago I had never run an AI model on my own machine. Every project I had ever built phoned a cloud API with a key sitting in it. Then I sat in the vibecoding lab at &lt;a href="https://www.amd.com/en/corporate/events/advancing-ai.html" rel="noopener noreferrer"&gt;AMD's Advancing AI 2026 summit&lt;/a&gt; and watched a model answer a question from a laptop, nothing leaving the room, and I got the itch.&lt;/p&gt;

&lt;p&gt;I have a pattern. When something lights me up, I chase it before I am ready. This was that. I picked a project that was over my head on purpose, and then I spent a week refusing to put it down.&lt;/p&gt;

&lt;p&gt;So before any numbers, here is the crux of it: the numbers are not the point, well kind of they are. The point is that I kept hitting walls and kept going anyway. The rest of this is just how.&lt;/p&gt;

&lt;h2&gt;
  
  
  I picked a project I had no business starting.
&lt;/h2&gt;

&lt;p&gt;The idea was model archaeology. Take a model from 2019 and a model from 2025, run them both on the same laptop, and measure what six years actually changed. I called it Second Squeeze. Squeeze an old lemon, squeeze a new one, see if the juice is the same.&lt;/p&gt;

&lt;p&gt;Old lemon: GPT-2 XL, 2019, 1.5 billion parameters. New lemon: Qwen3-0.6B, 2025, less than half the size. Both running locally through Lemonade, which is AMD's local AI server. I had never installed anything like this in my life. I started anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  The install fought me, and I kept going.
&lt;/h2&gt;

&lt;p&gt;It did not go smoothly. But I'm always up for a challenge! The command was not the one the docs implied. It was not on my PATH the first time. A GPU backend tried to download and jammed on a locked file, twice. My Python was too new for the eval tool, so I stood up a second Python just for this project.&lt;/p&gt;

&lt;p&gt;None of that is glamorous. But figuring it out is all the fun! Every wall was the same small decision: close the tab, or read the error and try one more thing. I kept picking one more thing. Eventually a tiny 2025 model said hello to me from my own laptop, and I may have made a noise, HOORAY!&lt;/p&gt;

&lt;h2&gt;
  
  
  My first real plan hit a wall, so I moved the wall.
&lt;/h2&gt;

&lt;p&gt;The original plan was accuracy. GPT-2 has a famous 2019 benchmark, and I wanted to see if the old claim held up. That test needs a specific kind of probability data from the model, and Lemonade did not hand it back the way the standard tool expected. Dead end.&lt;/p&gt;

&lt;p&gt;Old me would have read that as proof I was not cut out for this. Claude and new me read the docs instead, pinned down exactly what was missing, reproduced it cleanly, and filed an issue on Lemonade's repo so the next person hits a signpost instead of a wall. My first open source contribution, born entirely from something not working.&lt;/p&gt;

&lt;p&gt;Then I changed the question to one the tool could answer. Not how smart, but how fast, and can it even follow an instruction. Same curiosity, different door.&lt;/p&gt;

&lt;h2&gt;
  
  
  My own benchmark caught me lying, and I let it.
&lt;/h2&gt;

&lt;p&gt;This is the part I am proudest of, and it started as a mistake.&lt;/p&gt;

&lt;p&gt;My first speed run had GPT-2 at 0.31 tokens per second. Next to the new model's 90, that is a 297x gap. Huge. I wanted it to be true so badly.&lt;/p&gt;

&lt;p&gt;Before I posted it, Claude wrote a little script so anyone could re-run every number with one command. I ran it. GPT-2 came back at 8.5, not 0.31. My first number was junk, measured while a backend was still downloading and the model was cold off my disk. The real gap is about 11x, not 297x.&lt;/p&gt;

&lt;p&gt;I could have kept the 297x. Nobody would have checked. Instead I kept the 11x and wrote down why the first one lied, because a number you cannot reproduce is not a result, it is a rumor. The tool I built to keep myself straight is the tool that caught me. That felt like the whole month in one moment.&lt;/p&gt;

&lt;p&gt;And the capability side, quickly, because it made me laugh: I asked both models to finish "The capital of France is." The 2025 one said Paris and even showed a little reasoning first. The 2019 one, which has no concept of a conversation, produced a wall of random import statements. Six years is a long time.&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%2Fgnx5uvo438fs16l1zcn4.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%2Fgnx5uvo438fs16l1zcn4.png" alt=" " width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I kept wasn't the number.
&lt;/h2&gt;

&lt;p&gt;Here is what I took from the build, and it is not tokens per second.&lt;/p&gt;

&lt;p&gt;Every single step was something I had not done before. Running a model locally. Reading a stack trace with Claude's assistance. Filing an issue on Lemonade's repo. Saying in public that my own headline was wrong. Any one of those was a fine place to quit, and the only thing that made this a project instead of a folder of half-attempts is that I did not quit.&lt;/p&gt;

&lt;p&gt;I think that is the skill I love strengthening, more than any framework. Keep trying the new thing. Keep pushing when it pushes back. Change the door when the door is locked, but do not leave the building.&lt;/p&gt;

&lt;p&gt;Second Squeeze is live. There is a dashboard you can click, a repo you can re-run, tests you can try to break, and an issue I filed that might help close a real gap. Next I want to run the whole thing on an actual AMD GPU and add another bar. That is another burst, another day.&lt;/p&gt;

&lt;p&gt;If you have been circling something that feels over your head, take this as your sign. Start it before you are ready. Hit the walls. Keep going.&lt;/p&gt;

&lt;p&gt;For you, if you want it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live dashboard: &lt;a href="https://earlgreyhot1701d.github.io/second-squeeze/" rel="noopener noreferrer"&gt;https://earlgreyhot1701d.github.io/second-squeeze/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repo (MIT): &lt;a href="https://github.com/earlgreyhot1701D/second-squeeze" rel="noopener noreferrer"&gt;https://github.com/earlgreyhot1701D/second-squeeze&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The issue I filed: &lt;a href="https://github.com/lemonade-sdk/lemonade/issues/2822" rel="noopener noreferrer"&gt;https://github.com/lemonade-sdk/lemonade/issues/2822&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quick context if you are new here: I came to code from the courtroom. Jury services to AI builder in about a year, self-taught, learning in public. I direct, the agents generate, I validate and decide. That is the lens I am writing from.&lt;/p&gt;

&lt;p&gt;AI Assisted. Human Approved. Powered by NLP.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>showdev</category>
      <category>buildinpublic</category>
      <category>lemonade</category>
    </item>
    <item>
      <title>AMD Advancing AI 2026: Software, Hardware &amp; Framework, Unified</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Thu, 23 Jul 2026 22:46:16 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/amd-advancing-ai-2026-software-hardware-framework-unified-2d2j</link>
      <guid>https://dev.to/earlgreyhot1701d/amd-advancing-ai-2026-software-hardware-framework-unified-2d2j</guid>
      <description>&lt;p&gt;&lt;em&gt;Where &lt;a href="https://www.nondot.org/sabre/" rel="noopener noreferrer"&gt;Chris Lattner&lt;/a&gt; calls AI "mid" and &lt;a href="https://github.com/geohot" rel="noopener noreferrer"&gt;George Hotz&lt;/a&gt; wants to knock a trillion dollars of value off of NVIDIA with tinygrad.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Preamble
&lt;/h2&gt;

&lt;p&gt;IMHO: AMD's &lt;a href="https://www.amd.com/en/corporate/events/advancing-ai.html" rel="noopener noreferrer"&gt;Advancing AI 2026&lt;/a&gt; summit in San Francisco was free. And right now, while the firehose of free AI events and learning is still on, I'm taking every opportunity I can get my hands on. Eventually the free opportunities will slowly disappear, and this age of companies wanting us to adopt their tech and ecosystem will end. Cynical, pessimistic maybe. But think about the money spent on a two-day event at the Moscone Center. Free if you sign up. Free learning, access to vendors, tech talks, keynote speakers, food and lunch provided. That's an investment, and eventually the accountants, or the shareholders, are going to need to see a return. But I digress. This is my rundown of day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  AMD changed my mental model
&lt;/h2&gt;

&lt;p&gt;To say I'm a fish out of water at any event is an understatement. AWS, MLH, Google, and now AMD, I've tackled each one as a way to explore this brave new world, but sometimes I just don't have the context. And that's okay.&lt;/p&gt;

&lt;p&gt;AMD was more technical. More focused on hardware. There was more than one robot. And it changed my mental model of technical ecosystems and the symbiosis within them, and I'm so here for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A panel with no theme, and then Chris Lattner called AI "mid"
&lt;/h2&gt;

&lt;p&gt;Day one started with a three-person panel: Chris Lattner, Ramin Hasani, and Hassan Akbari, hosted by, checks notes, a name I didn't catch. There was no real theme. It was a roundtable discussion, and here's what came out of it.&lt;/p&gt;

&lt;p&gt;Chris Lattner created &lt;a href="https://llvm.org" rel="noopener noreferrer"&gt;LLVM&lt;/a&gt;, the compiler infrastructure a huge amount of modern software quietly runs on. So when he talks, I try to keep up. His point wasn't only about software. It was about how the hardware we run works with the applications we write, and why you can't reason about one without the other.&lt;/p&gt;

&lt;p&gt;He said he'd been told his approach wouldn't work, because &lt;a href="https://developer.nvidia.com/cuda-zone" rel="noopener noreferrer"&gt;CUDA&lt;/a&gt; has the moat. His answer, the way I understood it: CUDA is kind of like GCC. GCC is the decades-old open-source compiler a lot of software gets built with, so, big and foundational and hard to dislodge. A monolith. Powerful, legacy, wrapped in an incredible moat, and also not that good. His point: you don't beat the moat head-on, you go around it with architecture. Let the hardware express what it can do instead of locking everyone into one vendor's language. That's his whole &lt;a href="https://www.modular.com/mojo" rel="noopener noreferrer"&gt;Mojo&lt;/a&gt; play at &lt;a href="https://www.modular.com" rel="noopener noreferrer"&gt;Modular&lt;/a&gt;, for what it's worth, a portable alternative to CUDA.&lt;/p&gt;

&lt;p&gt;Then he dropped the line. AI is mid.&lt;/p&gt;

&lt;p&gt;Appropriate laughter followed. But I don't think he meant it as a burn. The way I read it, he meant AI is a distribution follower. The AI most of us touch every day, the LLMs, the predictive models, those are the product. They ride on top of the work happening behind the scenes: the training, the hardware, the compute. He lives in that granular layer. Most of us live in the public one. So when he says mid, I think he's talking about the surface, not the machine underneath it.&lt;/p&gt;

&lt;p&gt;Maybe I'm getting his intent wrong. I'm allowed. But that's the read I walked away with, and it stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ramin Hasani talked over my head, and I wrote it down anyway
&lt;/h2&gt;

&lt;p&gt;Ramin Hasani talked in abstractions I didn't fully follow. And I mean abstract for me, not abstract in general. The concepts were above where I am right now. But I wrote them down anyway, because future me is going to want them.&lt;/p&gt;

&lt;p&gt;Here's what I caught, in the order my pen caught it. Think about the problem at the algorithmic level before you reach for kernel optimizations. AI designing AI, and not just attention, look at the other transformers. Liquid foundation models, which are his company &lt;a href="https://www.liquid.ai" rel="noopener noreferrer"&gt;Liquid AI&lt;/a&gt;'s whole thing, so that one I could at least place. Matching the best models to the right hardware: CPU, NPU, GPU.&lt;/p&gt;

&lt;p&gt;I'm not going to pretend I can explain all of that yet. I can't. But it's in the notebook for when I get to the "kernel" level of my studies. Lol.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hassan Akbari tied it together
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/hassan-akbari-48a1b270/" rel="noopener noreferrer"&gt;Hassan Akbari&lt;/a&gt; was the one who tied it together. He talked about a unified ecosystem between frameworks, hardware, and software. He mentioned kernel again, and no, I still didn't catch the context, so onto the pile it goes with Ramin's. His point was that the interconnectivity of all three has to be leveraged for optimization. You don't tune one piece in isolation. You use how they connect.&lt;/p&gt;

&lt;p&gt;Then he asked the question that stuck with me. Large models can now be distilled into smaller ones that are still effective. So are we wasting compute? Scaling models, the way Hassan framed it, should be pragmatic. That one made sense to me.&lt;/p&gt;

&lt;p&gt;His other points came from the customer side, where reliability, cost per token, and accuracy are what matter. Optimization is the whole goal, and evaluation pipelines are how you measure it: benchmarks, deployments, the numbers. At least that's what I wrote down. Correct me if I mangled it.&lt;/p&gt;

&lt;h2&gt;
  
  
  George Hotz wants to knock a trillion dollars off NVIDIA
&lt;/h2&gt;

&lt;p&gt;Picture the stereotypical genius from Silicon Valley, the show. That's George Hotz. It was awesome, it was inspiring, and it was very on the nose. Note to self for the day: I am sharing a room with very, very, very smart people.&lt;/p&gt;

&lt;p&gt;Even his intro was wild. Jailbroke the iPhone. Reverse-engineered the PlayStation 3. Made a self-driving car , &lt;a href="https://comma.ai" rel="noopener noreferrer"&gt;comma.ai&lt;/a&gt;, that got a cease-and-desist letter from the DMV. Got hired to hack a company's own systems to keep them safer. I could not tell if I was watching an inspirational documentary or a cautionary afterschool special. But I already said he was awesome, and I stand by it.&lt;/p&gt;

&lt;p&gt;His talk was deep. Graphs, lines of code, evaluations. I have pictures, if anyone wants them. At one point he said the &lt;a href="https://en.wikipedia.org/wiki/Radeon_RX_7000_series" rel="noopener noreferrer"&gt;Radeon RX 7900 XTX&lt;/a&gt;, three years old now, is still a good value at $999. I wrote down "what is a 7900 XTX?" and filed it for later. Back to it.&lt;/p&gt;

&lt;p&gt;George didn't hold back, and the candor was refreshing. &lt;a href="https://github.com/tinygrad/tinygrad" rel="noopener noreferrer"&gt;tinygrad&lt;/a&gt;, which I looked up, is an open-source neural network framework and deep learning library, and on stage he put it at 25,000 lines of pure Python, about 9,000 of that the core, meaning the essential engine with everything else built around 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%2Fmgovzwznr29l632q8j1z.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%2Fmgovzwznr29l632q8j1z.jpg" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;His stated goal, and I quote, is to "commoditize the petaflop." Translation, delivered deadpan: if tinygrad succeeds, it knocks a trillion dollars of value off &lt;a href="https://www.nvidia.com" rel="noopener noreferrer"&gt;NVIDIA&lt;/a&gt;. Insert audience laughter here. Same moat Chris Lattner was talking about. George just comes at it from the scrappy open-source end.&lt;/p&gt;

&lt;p&gt;His pitch is GPUs for the middle class. He talked about growing up in New Jersey with a parent who was a teacher. He doesn't care about data centers. What he cares about is the highest development velocity, and not the out-of-the-box-fast kind. His argument, the way I understood it: an out-of-the-box &lt;a href="https://www.tensorflow.org" rel="noopener noreferrer"&gt;TensorFlow&lt;/a&gt; might be faster on day one, but tinygrad's velocity curve is built to climb higher and faster over time. And here's the part that got me. tinygrad has no dependencies. None. No numpy was used in the making of tinygrad. Which I learned is the whole flex. No dependencies means nothing underneath you can break, bloat, or drift out of version. Fewer moving parts, less that can rot. Very much my kind of principle. He even mentioned running tinygrad as a backend, which I am absolutely going to go try.&lt;/p&gt;

&lt;p&gt;So the guy who reverse-engineered a PlayStation had my full attention. So glad I made it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you see why I love these events?
&lt;/h2&gt;

&lt;p&gt;Here's what kept pinballing around my head after all of that.&lt;/p&gt;

&lt;p&gt;I'm an AI-assisted builder. I live on the top-level software end. I write code, I deploy applications. So why haven't I been more thoughtful about what runs underneath all of it? My dev.to articles focus on the end product, the thing I shipped. Not the framework, not the hardware, not the compute it all rides on.&lt;/p&gt;

&lt;p&gt;Maybe that's the next stage of my development. Or maybe not. Here's my honest worry: if I try to hold every piece at once, framework and hardware and software and infra, I might get so overwhelmed I don't build anything at all.&lt;/p&gt;

&lt;p&gt;And then the comforting thought. Maybe the three of them were mostly talking about training models, and I can go back to coding with vibes. The event leaned hard into CPU, GPU, NPU, compute, and infrastructure, not so much the SaaS layer I live in. So maybe it isn't my fight yet. Something to ponder.&lt;/p&gt;

&lt;p&gt;But that pinball is the whole reason I go. For a few hours, something pulled me out of my product-focused viewpoint and made me look at the layer underneath. That's the value. Not the swag bag, necessarily!&lt;/p&gt;

&lt;h2&gt;
  
  
  The workshops, and the one that masqueraded
&lt;/h2&gt;

&lt;p&gt;After the opening session I hit the workshops. One was Build Your OpenClaw Agent with Multi-Modal Models, running on AMD GPUs. Another was billed as vibecoding with local models. Good practice, all of it technology I hadn't touched. AMD's learning platforms were new to me. Working out of Jupyter notebooks was new too.&lt;/p&gt;

&lt;p&gt;The vibecoding workshop, well, the title masqueraded it. It was a &lt;a href="https://github.com/lemonade-sdk/lemonade" rel="noopener noreferrer"&gt;Lemonade&lt;/a&gt; and &lt;a href="https://github.com/QwenLM" rel="noopener noreferrer"&gt;Qwen&lt;/a&gt; workshop, and it rocked. First time I'd heard of Lemonade. It's a community project, sponsored by AMD with optimizations from their engineers, that runs large language models locally on your own GPU or NPU. We ran Qwen3.6-35B-A3B on it. They walked the framework from the ground up: silicon, engines, routing, models, then the app. Another pyramid. And there I was again, looking down from my product endpoint at the top and seeing how much sits underneath. Same shift as the panel, twice in one day.&lt;/p&gt;

&lt;p&gt;This one got me excited though. Running Lemonade locally means everything stays with me. A new idea, a new system, a new output to go explore. I'm sure the learning curve will be steep. We were working inside pre-set-up AMD environments, and standing something up from scratch will bring its own challenges. But as always, excited and ready to try something new.&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%2Fwlvw8arrtg1pwvileko0.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%2Fwlvw8arrtg1pwvileko0.jpg" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Full disclosure: I did day one as a day trip, LAX to SFO and back. Day two happens without me, and I'm bummed to miss it. I'll have to watch &lt;a href="https://www.youtube.com/live/jvtPC28nGsc?si=8PPIaCjFY0HQgmwI" rel="noopener noreferrer"&gt;Dr. Su's keynote&lt;/a&gt; on Youtube.&lt;/p&gt;

&lt;p&gt;What I'm carrying home instead is the Lemonade itch. A from-scratch local setup on my own machine, which will have its own learning curve. I'll report back on how that goes.&lt;/p&gt;

&lt;p&gt;Until the next event! I hope to see my Dev.to fam there, too! &lt;/p&gt;

&lt;p&gt;AI Assisted. Human Approved. Powered by NLP.&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%2F3op2c0ptyyvtxjosclb6.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%2F3op2c0ptyyvtxjosclb6.jpg" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>learning</category>
      <category>buildinpublic</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Open AI Build Week: Foundation First, Software Second</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Tue, 21 Jul 2026 04:34:11 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/open-ai-build-week-3fi8</link>
      <guid>https://dev.to/earlgreyhot1701d/open-ai-build-week-3fi8</guid>
      <description>&lt;p&gt;IMHO, there's a rush to sell AI to government right now (note: I work in government). I kept asking a different question: has anyone checked whether government is &lt;em&gt;ready&lt;/em&gt; to buy?&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://verity-lex.vercel.app" rel="noopener noreferrer"&gt;Verity Lex&lt;/a&gt; over about two days of OpenAI Build Week to answer it, and one rule ended up shaping the whole build: the AI reads the court's record, but it never assigns the score. That sounds like a small design choice. It's the build, and teaching a machine to respect it was the hard part, and the one that cost me a stressful evening I could have skipped. Point Verity Lex at a California superior court (Santa Barbara is live, more courts to come) and it reads the public record, checks it against published legal standards, and returns an AI-readiness score anyone can recompute.&lt;/p&gt;

&lt;p&gt;This is the story of what I built, how I directed Codex and GPT-5.6 to build it, and the one lesson I'd like to pass on to any of my Dev.to fam shipping an AI.&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%2F5knw9s0jtdahfpjfhivm.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%2F5knw9s0jtdahfpjfhivm.png" alt=" " width="800" height="414"&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%2Fy29drx3ze2vlclk2ceek.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%2Fy29drx3ze2vlclk2ceek.png" alt=" " width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The thesis: you're selling to government backwards
&lt;/h2&gt;

&lt;p&gt;Software gets sold to public institutions the wrong way around. A vendor shows up with a solution and goes looking for a problem, without ever understanding the institution it's selling into. What is this court held to? Is it meeting that standard? Are its people ready for the thing you want to sell them? Skip those questions and you get shelfware and eroded trust, which anyone who has worked in the public sector has watched happen.&lt;/p&gt;

&lt;p&gt;AI is about to run that same play at scale. So Verity Lex starts at the bottom of a pyramid, not the top:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Standards.&lt;/strong&gt; What is this institution held to, and does its own record show it's meeting it? &lt;em&gt;This is Verity Lex.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure.&lt;/strong&gt; Is the organization itself ready? Governance, roles, operational policy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;People.&lt;/strong&gt; Do staff know how to use AI safely? Where are the culture blockers?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solutions.&lt;/strong&gt; Only now do you get to sell something, prescribed against a diagnosed picture instead of a pitch deck's guess.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You earn the right to sell to government by understanding it first. Verity Lex is tier one, shipped.&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%2Fhkkcc8kme29ri0m4zfqz.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%2Fhkkcc8kme29ri0m4zfqz.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture: neurosymbolic on purpose
&lt;/h2&gt;

&lt;p&gt;Here's the trap most AI demos fall into: they put the model in charge of the answer. Ask an LLM to score a court's compliance and you get a number shaped by whatever the sampling did that second, with citations it might have dreamed. Run it twice, get two answers. No government buyer can procure "the AI felt good about our compliance."&lt;/p&gt;

&lt;p&gt;So I split the job into three, along one boundary: perception versus judgment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Finding the documents: GPT-5.6 directs, Tavily retrieves.&lt;/strong&gt; A model-directed ReAct loop decides where to look. GPT-5.6 reasons about the next move; Tavily runs the retrieval, searching the court's public site and pulling documents a plain fetch can't reach. Tavily was new to me going in, and giving the model a real search-and-extract layer instead of a hand-rolled fetch is a chunk of why the agent can find a document that moved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reading the documents: GPT-5.6 extracts.&lt;/strong&gt; It reads what comes back and pulls evidence into a strict JSON schema. Finding and reading are both perception, and perception is what a model is good at.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoring the readiness: a deterministic rule engine.&lt;/strong&gt; Pure TypeScript, no model imported anywhere in it. It takes the extracted signals and applies a published registry of weighted, legally-grounded artifacts. Same signals in, same score out, forever. Scoring is judgment, and judgment about a number has to be reproducible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two of those jobs are perception, one is judgment, and the design lives on keeping them apart.&lt;/p&gt;

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

&lt;p&gt;Would you trust an AI to grade its own homework? Neither would a court. The model has no path to the score, by construction. Every finding cites a real document and a quoted line. Anything it can't find is marked &lt;em&gt;not located&lt;/em&gt;, never &lt;em&gt;absent&lt;/em&gt;, because a public record going quiet isn't proof of anything. And you can download an audit bundle and recompute the score yourself. The constraint is the product.&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%2Fm2lk3w5no0klutj9h3tg.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%2Fm2lk3w5no0klutj9h3tg.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I built it: directing Codex
&lt;/h2&gt;

&lt;p&gt;I built this with Codex in VS Code using what I think of as a creative-director workflow. I own the judgment. Codex owns the implementation. The boundary between us is enforced, not trusted.&lt;/p&gt;

&lt;p&gt;In practice that meant gated, block-scoped prompts. Every one started with "propose a file plan first, do not refactor unrelated code, stop after the PR." Codex built each block, the rule engine, the agent tools, the loop, the API, the hardening, CI, the add-ons, as its own pull request. I reviewed and tested every one before it merged, and CI enforced that no red PR reached main. The commit history is the collaboration log: over twenty scoped PRs, each one a discrete piece of the argument.&lt;/p&gt;

&lt;p&gt;Codex was good. It caught a &lt;code&gt;next/server&lt;/code&gt; import trap and fixed it by using web-standard &lt;code&gt;Response.json&lt;/code&gt;. It diagnosed a lockfile mismatch that only showed up on the Linux CI runner. And when it was wrong, I overruled it, which is the entire point of keeping a human at the gate. At one point it proposed swapping deterministic installs for a looser command to "fix" a CI failure. That would have traded away reproducibility for a problem we hadn't even diagnosed. We diagnosed it instead. It was a stale cache.&lt;/p&gt;

&lt;p&gt;GPT-5.6 runs live inside the shipped product, directing discovery and extracting evidence, with Tavily as the retrieval layer underneath it, and it is architecturally barred from touching the score.&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%2Fz0t2y6uamhmcqjx4o140.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%2Fz0t2y6uamhmcqjx4o140.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned: my tests were green the whole time. Then I deployed.
&lt;/h2&gt;

&lt;p&gt;Here's the part I'd do differently. If you're building something similar, maybe it saves you the evening it cost me.&lt;/p&gt;

&lt;p&gt;At first, my CI was green. Rule engine tested, agent loop tested, bounds tested, API contract tested. I read that green as "it works." Then I set my real API keys in production, clicked the button, and watched it fail three different ways in a row.&lt;/p&gt;

&lt;p&gt;First a 400 from the model: OpenAI's JSON mode requires the word "json" to appear in the input itself, and my instruction saying so in a separate field didn't count. Then the agent's planner returned an action with no query attached, so every search got rejected and the scan found nothing. Then the extractor produced JSON in the wrong shape and my validator threw on every document.&lt;/p&gt;

&lt;p&gt;Three bugs, one root cause: &lt;strong&gt;every one of them lived at the boundary between my code and the real model, and my stub had hidden all of them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's the precise miss. My stub model always returned correctly-shaped, pre-baked responses. So my tests proved my &lt;em&gt;code&lt;/em&gt; could consume a good model answer. They never proved my &lt;em&gt;prompts&lt;/em&gt; could produce one. Those are different claims. My planner test literally handed the stub an action that already had the required fields, so it passed, while the actual prompt never told a real model those fields existed. The test fixture quietly knew something the prompt didn't teach.&lt;/p&gt;

&lt;p&gt;I fixed the three shape bugs. And then a worse one showed up, because I'd finally learned to actually measure: I ran the same scan five times and got different scores. Fifty-nine one run, sixty-seven the next. Same court, same day.&lt;/p&gt;

&lt;p&gt;My first guess was budget, that the agent ran out of steps before covering everything. Wrong. I handed it a generous budget and it still wandered, using a fraction of it and stopping in different places each run. The problem wasn't resources. I was asking a non-deterministic model to do a deterministic job, march through nine known legal standards and check each one, and a model does not march. It strolls. More budget just bought a longer runway. Oops!&lt;/p&gt;

&lt;p&gt;Thereason Verity Lex is "trustworthy" is that I'd taken the &lt;em&gt;score&lt;/em&gt; away from the model and given it to deterministic code. So my first instinct was: do the same to the search. Write the queries in code, pick the links in code, let the model only read. Cage it into repeating itself. I even confirmed the model wouldn't help me the easy way, GPT-5.6 doesn't expose a temperature setting, so I couldn't just turn its randomness down.&lt;/p&gt;

&lt;p&gt;I got most of the way to convincing myself to build the cage before I saw the trap. A court renames a policy PDF, or moves it to a new page, and a hardcoded search walks right past it. The free, uncaged agent, reading the results and reasoning about them, finds it at the new location. Caging the agent would make it repeatable and blind, blind to exactly the thing this product exists to catch: change. The improvisation I was trying to delete was the value.&lt;/p&gt;

&lt;p&gt;So the lesson is more careful than "make everything deterministic." Move a guarantee into code when a second answer is simply wrong, scoring, where two numbers for the same evidence is a bug by definition. But where the adaptivity itself is the value, like finding a document that moves, don't cage it. Make it reliable a different way.&lt;/p&gt;

&lt;p&gt;And the different way is the thing I'd cut from v1 for simplicity: memory. The scan wanders because it's stateless. It re-improvises the entire hunt every single time, starting from nothing. Ugh, my design bad. A version that remembered what it found last time wouldn't re-hunt a known policy, it would re-verify it, and when the policy moved it would notice the move instead of silently missing it. The variance I'd been treating as a defect turns into signal: convergence when the record holds still, a change alert when it doesn't. The fix for an unreliable observer was never to cage it. It was to give it a memory. That's the top of the roadmap now, and it's the sharpest thing this whole build taught me: a stateless observer cannot repeat itself, and memory, not a cage, is what makes an adaptive agent reliable. &lt;strong&gt;EUREKA&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The practical habits that would have caught all of this earlier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smoke-test the model interface the day it's written, not the day it deploys.&lt;/strong&gt; One real-key call doing one discover and one extract would have surfaced all three prompt bugs ealier, calmly, instead of at 11pm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write prompt-contract tests that never call the API.&lt;/strong&gt; Assert the extractor prompt actually contains the schema field names and the valid IDs. Cheap, static, catches underspecification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Record the stub's responses from one real call and freeze them,&lt;/strong&gt; so the stub can't drift into being more forgiving than reality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run anything non-deterministic several times and assert the spread is bounded.&lt;/strong&gt; Variance is invisible to a single run, and a stub can never show it to you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-derive tuned parameters when their inputs change.&lt;/strong&gt; My search budget was set when the registry was smaller and never revisited when it grew.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An ode to determinism: the deterministic core had zero production bugs. The guardrails held even while the model layer was failing. Every bug degraded gracefully, structured errors and honest empty states, never a crash, because the fail-safe design was real. And once I added one line of error logging, each mystery became a one-paste diagnosis. The architecture was resilient and the bugs were findable, which is a far better place to be than a fragile system hiding quiet ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  The category I didn't see until I'd built it.
&lt;/h2&gt;

&lt;p&gt;Here's the last thing the build taught me, and I was shook. For most of it I thought I was making a SaaS: point it at a court, get a score. Judged as that, the variance really was fatal. Run it twice, get two numbers, look broken.&lt;/p&gt;

&lt;p&gt;Then I actually looked at what I'd built. A human gate on verification. A draft-inquiry workflow. Findings meant to be reviewed and confirmed, not consumed. Those aren't consumer-SaaS features. They're the features of an analyst's tool. I hadn't built a one-click verdict machine. I'd built readiness intelligence, the kind of thing a vendor runs (oh, the irony), reviews, and tracks over time, and I just missed the point entirely at first.&lt;/p&gt;

&lt;p&gt;That reframe dissolved the thing I'd been panicking about. A business-intelligence tool is &lt;em&gt;supposed&lt;/em&gt; to be run repeatedly. You pull the data, you review it, you watch the trend. Variance between pulls isn't a defect, it's the raw material of a baseline. And the credibility was never in the summary number anyway. It's in the citations: every finding links to a real document and a quoted line you can open and check. The number can move. The evidence doesn't lie.&lt;/p&gt;

&lt;p&gt;You rarely see the category up front. You build into it, and one day you look up and realize what you actually made. Naming it right, mid-build, under a deadline, turned my biggest liability into the most honest thing about the product.&lt;/p&gt;

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

&lt;p&gt;Verity Lex today is one court, scored against one registry version, on a public-record foundation, a live observer that reads the record fresh each time. It's stateless: nothing is stored yet, and it re-hunts from scratch every run. Everything past this point is roadmap, not shipped.&lt;/p&gt;

&lt;p&gt;Two things sit at the top of it, and both come straight out of the variance. First, ensemble extraction. GPT-5.6 exposes no temperature control, so a single reading of a document varies run to run. Running each extraction several times concurrently and accepting a signal only on a majority vote tightens the score without caging the agent, in parallel, so it costs time you won't feel. &lt;/p&gt;

&lt;p&gt;Second, and the one the lesson pointed to: memory. v2 would write every scan to an append-only log first, a side effect that can't touch the scan itself, then turn on the read, and that's where it stops being a one-shot scanner and becomes something worth paying for. Scan a court a few times and the baseline strengthens and converges. Store it and you can tell when the record moves, a policy posted, a plan pulled down. The agent stays free to hunt, because hunting a moving target is the point, and memory is what turns its wandering into a baseline you can trust and a change feed you can watch. Above that sit the tiers left as roadmap: structure, people, solutions, deliberately not half-built.&lt;/p&gt;

&lt;p&gt;But the wedge is real, it's live, and it holds the line it was built to hold: the AI reads the record, and it never assigns the score.&lt;/p&gt;

&lt;p&gt;*&lt;a href="https://devpost.com/software/verity-lex-see-a-court-on-the-record" rel="noopener noreferrer"&gt;Built for OpenAI Build Week 2026 with Codex, GPT-5.6, and Tavily. &lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI assisted. Human approved. Powered by NLP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://verity-lex.vercel.app" rel="noopener noreferrer"&gt;verity-lex.vercel.app&lt;/a&gt;*&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openapi</category>
      <category>showdev</category>
      <category>govtech</category>
    </item>
    <item>
      <title>AWS Weekend Agent Challenge: Daybreak</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Sat, 18 Jul 2026 04:12:39 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/aws-weekend-agent-challenge-daybreak-45dc</link>
      <guid>https://dev.to/earlgreyhot1701d/aws-weekend-agent-challenge-daybreak-45dc</guid>
      <description>&lt;h2&gt;
  
  
  Vision and what the agent does
&lt;/h2&gt;

&lt;p&gt;I build fast and I build a lot. As of this weekend that is 49 public repos. What I am bad at is remembering the state of any of them. Which ones are live, which shipped with no README, which went quiet months ago and quietly rotted. The answer used to be that I had no idea until I went digging.&lt;/p&gt;

&lt;p&gt;Daybreak is the fix. I wanted the review done before I even sat down: not a dashboard I have to remember to check, but a brief already in my inbox, waiting at first light. That is the name. Every morning at 6 AM Pacific it wakes up on its own, with no button to press. It takes inventory of every repo on my GitHub account, reasons over that inventory with Amazon Nova, and leaves a short brief in my inbox: how many repos are live, how many have gone quiet, how many are missing a README or a license, and three specific "do something here" nudges. By the time I am awake, the review is done and waiting. The best tool is the one you never have to open.&lt;/p&gt;

&lt;p&gt;It runs read-only. It reports, I decide. It never touches my repos.&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%2Fa93ue8q8emqbjpjfp3v0.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%2Fa93ue8q8emqbjpjfp3v0.png" alt=" " width="799" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I built it
&lt;/h2&gt;

&lt;p&gt;PRD first, always. One architecture doc with MUST, STUB, and NEVER labels before a line of code, then a block-by-block build with a pass or fail checkpoint after each block. Uptime checks, showcase scraping, and auto-generated README pull requests are all stubbed with implementation notes, not half-built. Shipping small and lean beat shipping big and broken.&lt;/p&gt;

&lt;p&gt;I ran it as a multi-model workflow. Claude for the architecture and the PRD, Kiro for the spec-driven build. I wrote phase-aware guardrails so Kiro could do the file authoring and tests while I was at work, then flip to a go-live mode that let it deploy with my credentials once I got home. I direct and approve, the agents generate.&lt;/p&gt;

&lt;p&gt;The challenge worth mentioning was getting Nova to answer at all. My first invoke failed with "on-demand throughput isn't supported." Nova will not run on-demand from the bare model id. You have to call the cross-region inference profile, &lt;code&gt;us.amazon.nova-lite-v1:0&lt;/code&gt;, and your IAM policy has to allow both the profile and the underlying model in every region the profile routes to. List only the profile and you get AccessDenied even with model access on. Once I understood that, it answered first try.&lt;/p&gt;

&lt;p&gt;A second lesson landed mid-build. The old Bedrock model access page was retired in late 2025 and Amazon-provided models are auto-enabled now, so a step I had planned no longer existed. Checking whether a tool is current before depending on it saved me from chasing a dead console page.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS services used and architecture overview
&lt;/h2&gt;

&lt;p&gt;The whole thing is one Lambda and a schedule.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon EventBridge Scheduler&lt;/strong&gt; fires the function daily at 6 AM Pacific with a timezone-aware cron, so daylight saving handles itself. This is the trigger, not a button.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Lambda&lt;/strong&gt; runs the agent in Python with zero third-party dependencies, so the deploy package is a single zipped file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock&lt;/strong&gt; with Nova Lite via the Converse API does the reasoning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon SES&lt;/strong&gt; emails the brief.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon CloudWatch&lt;/strong&gt; logs every run and alarms on any error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Infrastructure is a single AWS SAM template, so the whole stack deploys and rolls back as one unit.&lt;/p&gt;

&lt;p&gt;Flow: EventBridge Scheduler triggers Lambda, which reads the GitHub API, calls Bedrock Nova, and sends through SES to my inbox.&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%2Fn04ekzhz4eqfa7l4rftt.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%2Fn04ekzhz4eqfa7l4rftt.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One design choice I am proud of: structure is deterministic, flavor is generated. The counts of how many repos are live, quiet, or unlicensed are computed in Python so they can never be hallucinated. Nova only writes the narrative and the nudges around those fixed numbers.&lt;/p&gt;

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

&lt;p&gt;Bedrock inference profiles, and why on-demand Nova needs the &lt;code&gt;us.&lt;/code&gt; prefix and IAM that covers the fan-out regions. Least-privilege IAM for an agent: four actions, no wildcards, with Parameter Store wired for secrets (the token slot exists but v1 runs unauthenticated just fine). SES sandbox is fine when sender and recipient are the same verified address, which skips a production-access request. And the value of a hard line between deterministic logic and model reasoning, so the numbers are trustworthy and the writing is still warm.&lt;/p&gt;

&lt;p&gt;The proof it ran without me: I set the EventBridge schedule to fire at 8:07 PM, stepped away, and it went off on its own. It pulled all 49 repos, Nova wrote the brief, and SES delivered it in about 4 seconds, with the CloudWatch timestamp and the email to match. That was a real scheduled trigger, not a button, which is the whole point of the challenge. With autonomy proven, I set the production schedule to 6 AM Pacific daily. The verbatim CloudWatch logs, timestamps, and request IDs are in &lt;a href="https://github.com/earlgreyhot1701D/daybreak/blob/master/PROOF.md" rel="noopener noreferrer"&gt;PROOF.md&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Link to repo
&lt;/h2&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/earlgreyhot1701D/daybreak" rel="noopener noreferrer"&gt;github.com/earlgreyhot1701D/daybreak&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Proof of autonomous run: &lt;a href="https://github.com/earlgreyhot1701D/daybreak/blob/master/PROOF.md" rel="noopener noreferrer"&gt;PROOF.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI Assisted. Human Reviewed. Powered by NLP. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>showdev</category>
      <category>buildinpublic</category>
      <category>agents</category>
    </item>
    <item>
      <title>MLH x DigitalOcean Hackathon: Jury Duty, Explained</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Sat, 11 Jul 2026 20:55:15 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/mlh-x-digitalocean-hackathon-jury-duty-explained-1e8p</link>
      <guid>https://dev.to/earlgreyhot1701d/mlh-x-digitalocean-hackathon-jury-duty-explained-1e8p</guid>
      <description>&lt;h1&gt;
  
  
  Justicia Clew: what a hackathon build teaches you that a tutorial never will
&lt;/h1&gt;

&lt;p&gt;This was my first SF hackathon. I was nervous going in, and not really about the code. I was nervous I'd get put on a team I didn't pick, or worse, that I'd have to pitch myself onto one. But, I was able to build solo, phew! Turned out to be the right call for a two-day sprint where I needed to move fast and make my own calls without a group chat to sync with first.&lt;/p&gt;

&lt;p&gt;Solo only gets you so far though. I need to build with someone eventually, just for the practice of it, doesn't have to be this project or even this kind of tool. So if anyone reading this wants to build something together sometime, my inbox is open.  &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%2F17yszkpv4c1lk4px0arx.gif" 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%2F17yszkpv4c1lk4px0arx.gif" alt=" " width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyhow, here's what I built: &lt;a href="https://justicia-clew-8f4vi.ondigitalocean.app/" rel="noopener noreferrer"&gt;Justicia Clew&lt;/a&gt; is a mobile-first web app that answers jury duty questions in your court's own words, not legal advice, just plain-language answers grounded in your actual county court's public website, with a real phone number when it doesn't know something instead of guessing. Built for Santa Barbara County as the live demo, on a FastAPI backend and DigitalOcean's Gradient AI agents, over about two days.&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%2Ft53a9s8sh35ig71xq4hn.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%2Ft53a9s8sh35ig71xq4hn.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The part nobody tells you: shipping infra means un-shipping it too
&lt;/h2&gt;

&lt;p&gt;Here's a moment from tonight that didn't make it into any tutorial I've read on "how to deploy your hackathon project."&lt;/p&gt;

&lt;p&gt;I finally had the whole pipeline working. Knowledge base grounded in real Santa Barbara jury services content, agent refusing cleanly when it didn't know something instead of guessing, frontend wired to real API calls instead of mock data. Time to deploy. I open DigitalOcean App Platform, pick my repo, and the console shows me a number: $24 a month.&lt;/p&gt;

&lt;p&gt;Immediate gut reaction: hell no.&lt;/p&gt;

&lt;p&gt;Then the actual thinking kicked in. That $24 is a monthly rate, not a bill for hitting deploy. DigitalOcean bills by the hour, prorated. Run it for a few hours during judging and destroy it after, and you're looking at pennies, not $24. I had $200 in hackathon credits sitting there too, so the dollar cost of tonight was never really the issue.&lt;/p&gt;

&lt;p&gt;The real issue was time I hadn't budgeted for. Because it turns out "deploy your AI app" isn't one task, it's two: deploy it, then remember to tear it back down. My $200 in hackathon credits expires July 13th, two days after the deadline. My knowledge bases run on a managed OpenSearch database, which is a second billable resource completely separate from the app itself, easy to forget exists once it's just quietly running in the background. So the actual finish line isn't "it's live." It's "it's live, and I've written myself a reminder to kill the App Platform app and the OpenSearch database before the credit window closes."&lt;/p&gt;

&lt;p&gt;Nobody puts that in the excited "I shipped my hackathon project!" post. It's not a dramatic problem. It's just real infrastructure behaving like real infrastructure: it keeps costing you something (money, attention, a reminder on your calendar) until you actively tell it to stop. A mockup doesn't have that problem. A mockup doesn't have a lot of problems, which is exactly why it's not the same thing as shipping.&lt;/p&gt;

&lt;p&gt;That's the honest cost of building with real tools instead of toy ones on a hackathon deadline. Not the $24. The fact that "done" has an expiration date you have to manage yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the AI can just... write to your repo
&lt;/h2&gt;

&lt;p&gt;Small moment, worth flagging because it changed how I thought about the workflow mid-build.&lt;/p&gt;

&lt;p&gt;Deploy failed at 10am the next morning. Build log showed a real, specific error: no Python version pinned, DigitalOcean's buildpack defaulted to the newest one available (3.14), and &lt;code&gt;pydantic-core&lt;/code&gt; (a dependency of a dependency) needed to compile from source using a Rust tool called PyO3, which doesn't support Python 3.14 yet. Clear root cause, one-line fix: add a &lt;code&gt;.python-version&lt;/code&gt; file pinning to 3.12.&lt;/p&gt;

&lt;p&gt;Except Claude didn't just tell me to make that file. It wrote it, sent it to me, and pushed it directly into my repo through the device connection, then handed me back three git commands to commit and push. I hadn't fully clocked that this was possible until it happened. My mental model going in was "AI writes code in a scratch space, I copy it in." Turns out the actual model, once you connect your machine, is closer to "AI can touch your files directly, and tells you what it did."&lt;/p&gt;

&lt;p&gt;That's not a complaint. It was the right fix, and I checked it was really there before pushing, same as I'd checked every Kiro claim all day. But it's worth naming as its own thing in a "creative director directs, AI builds" workflow: the line between "AI suggests, I execute" and "AI executes, I review" is thinner than it looks once your tools are actually wired together, not just chat-in-a-browser-tab. I stayed the one who ran &lt;code&gt;git push&lt;/code&gt;, so the last gate was still mine. But the boundary moved, and I didn't fully expect it to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scary-sounding fix that wasn't
&lt;/h2&gt;

&lt;p&gt;After deploying, I asked the live app a question I expected to work, "can my employer fire me for jury duty," and got a flat refusal. Then another one. And another. My first read was "our RAG is thin," like the retrieval itself was somehow bad.&lt;/p&gt;

&lt;p&gt;It wasn't a retrieval problem. It was a coverage problem. When I actually went and looked at what was in the knowledge base, I realized my fallback file upload (the workaround from when DigitalOcean's crawler couldn't fetch the live court page) only ever captured one tab of a six-tab page. The Employer Information tab, the one with the actual California Government Code language protecting jurors from being fired, had never made it in. Neither had the postponement/deferral process. The agent wasn't broken, it was accurately refusing to answer questions it genuinely had no source content for.&lt;/p&gt;

&lt;p&gt;The part I expected to be scary was fixing it: removing an existing data source from a knowledge base that's already attached to a live agent, and swapping in a corrected one. That sounds like exactly the kind of thing that could break a working system. It didn't. The knowledge base keeps its own ID no matter what data sources go in or out of it, the agent references that ID, not the individual files, and it reindexes and picks up new content automatically. No reattaching the agent, no redeploying the app, nothing. Remove the old source, add the new one, wait a minute for it to reindex, ask the same question again.&lt;/p&gt;

&lt;p&gt;The lesson wasn't really about DigitalOcean's API design, though that part holds up well. It was that "thin RAG" and "missing content" look identical from the outside, a flat refusal either way, and only one of them means your retrieval setup needs work. Go look at what's actually in the knowledge base before you assume the model's the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing in a fourth tool for the last mile
&lt;/h2&gt;

&lt;p&gt;By the time the app was deployed and mostly working, my usual three-tool split, Claude for architecture and docs, Kiro for the actual build, Gemini for visual and content, wasn't quite enough. I wanted an adversarial QA pass before submitting, something whose whole job was to try to break what Kiro had built, not build more of it.&lt;/p&gt;

&lt;p&gt;So I brought in Claude Code for that specifically: a code review, endpoint and input edge cases, a check on whether the app has any real protection against someone hammering it, and a walkthrough acting like an actual juror using the thing.&lt;/p&gt;

&lt;p&gt;First draft of that prompt asked it to actually generate the traffic itself, fire a real batch of requests at the live API to prove it holds up. Caught myself before sending it: the app calls a metered DigitalOcean agent per request, real hackathon credits, no rate limiting yet since that's an intentional stub for tonight. Actually hammering the live endpoint to test whether it can be hammered would have been a strange way to find out the answer is "not yet," at my own expense. Rewrote it as a report-only pass instead: read the code, reason about the exposure, name the gap plainly, don't go generate the problem to prove it exists.&lt;/p&gt;

&lt;p&gt;Small moment, but it's the same lesson as the deploy pricing scare earlier in the night. Real infrastructure means real consequences for careless testing, not just careless shipping.&lt;/p&gt;

&lt;h2&gt;
  
  
  The near-miss that would have quietly broken everything
&lt;/h2&gt;

&lt;p&gt;Somewhere in the middle of setting up knowledge bases for six counties, I almost combined all six into one.&lt;/p&gt;

&lt;p&gt;The Gradient AI Platform lets a knowledge base hold multiple data sources, which is exactly the right feature for the wrong moment. I had six county URLs open in six tabs, adding them one after another, and the flow doesn't stop you from adding source five and six to the same knowledge base you just created for source one. Nothing in the console yells "wait, these are supposed to be separate." I caught it staring at the confirmation screen before clicking create, because the whole architecture depends on county isolation coming from separate knowledge bases, one per county, each attached to its own agent. Combine them and a question about Santa Barbara parking could pull retrieved chunks from Fresno's page, with no clean way to tell the agent "only look at this part."&lt;/p&gt;

&lt;p&gt;Nothing bad happened. But it's the kind of mistake that doesn't announce itself later, it just quietly answers questions with the wrong county's information and looks like a retrieval quality problem instead of what it actually is, a setup mistake from two days earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  A URL that wouldn't load, for no reason the console would tell me
&lt;/h2&gt;

&lt;p&gt;The Gradient AI Platform's web crawler is supposed to pull a URL straight into a knowledge base. Mine kept failing on Santa Barbara's real, live, working jury services page. Not a typo, not a dead link, I could open it fine in a browser. The console just said it couldn't be fetched, no status code, no reason.&lt;/p&gt;

&lt;p&gt;My best theory, never confirmed, is bot protection on the court's side blocking whatever the crawler identifies itself as. There was no way to check that from the DigitalOcean side. So I worked around it: copied the page content by hand into a markdown file and uploaded that as the data source instead of the URL. It worked. It also cost me an hour figuring out that file upload was even an option, and then later cost me another round when my first copy-paste only grabbed one of six tabs on that page and missed the two sections (employer protections, postponement process) that mattered most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust, but check the file yourself
&lt;/h2&gt;

&lt;p&gt;This is the one I'd tell any solo builder using an AI coding assistant, no exceptions.&lt;/p&gt;

&lt;p&gt;More than once, Kiro reported a fix as done, tested, confirmed, here's the proof, and the actual file on disk still had the old code. Not once. At least three separate times, across different files, different fixes. The first time I assumed I'd misread something. By the third time I stopped assuming and started verifying every single claimed fix against the real file before moving on, no matter how confident the report sounded.&lt;/p&gt;

&lt;p&gt;Eventually I stopped routing some fixes through Kiro at all and had Claude write and commit them directly, then had Kiro independently check afterward, from a fresh read, no memory of writing them. That's not a knock on Kiro specifically. It's a rule I'd apply to any AI tool making changes I can't watch happen in real time: a reported fix is a claim, not a fact, until you've read the file yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cutting six counties down to one, on purpose
&lt;/h2&gt;

&lt;p&gt;The original scope was six counties and a bilingual interface. By the halfway point of day one, the honest read was that six full counties meant six half-working ones, and I'd rather ship one that actually works end to end than five that don't.&lt;/p&gt;

&lt;p&gt;So I labeled everything MUST, STUB, or NEVER before writing more code. Santa Barbara: MUST, fully built, real content, real agent, real answers. The other five counties: STUB, meaning the architecture supports adding them (one knowledge base, two environment variables, no code changes) but there's no content behind them yet. Spanish: STUB too, the toggle exists in the UI but it's disabled, because shipping a language switch that only translates button labels and not the actual answers would be worse than not having it. NEVER went on the list too: no legal advice, ever, no matter how the feature might tempt someone to add it later.&lt;/p&gt;

&lt;p&gt;Cutting scope on purpose, with the cuts written down instead of just quietly not happening, is the only reason this got submitted instead of half-finished.&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%2F7vcufi6eugfnvpvx6kx2.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%2F7vcufi6eugfnvpvx6kx2.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hitting submit
&lt;/h2&gt;

&lt;p&gt;I wrote at the top of this that I was nervous going in. Different kind of nervous now, the kind where you've just clicked submit on Devpost and there's nothing left to do but wait.&lt;/p&gt;

&lt;p&gt;Two days, zero prior DigitalOcean experience, one person, one county, one real working answer to a question that matters to nearly everyone eventually: what do I actually do about this jury summons. That's the whole thing. If you made it this far and you're someone who'd want to build something together sometime, the offer from the top of this post still stands.&lt;/p&gt;

</description>
      <category>digitalocean</category>
      <category>mlh</category>
      <category>showdev</category>
      <category>socialgood</category>
    </item>
    <item>
      <title>Fable 5 Hype: Fangirling with Datasets to Build a Lakers Dashboard</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Mon, 06 Jul 2026 04:05:07 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/fable-5-hype-fangirling-with-datasets-to-build-a-lakers-dashboard-3pdd</link>
      <guid>https://dev.to/earlgreyhot1701d/fable-5-hype-fangirling-with-datasets-to-build-a-lakers-dashboard-3pdd</guid>
      <description>&lt;p&gt;This is the story of a for-fun project, &lt;a href="https://luka-fit-index.netlify.app/" rel="noopener noreferrer"&gt;Luka Fit Index&lt;/a&gt; that started with me typing "ai for fun? picture this" at Claude Fable 5, Anthropic's new model, the one all the launch hype has been about. I wanted to see what the hype felt like on a project with zero stakes: no hackathon deadline, no rubric, just my team, my datasets, and an afternoon. It ended with a deployed page, a PRD, and a tool whose best feature is a paper trail of its own mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea that wasn't supposed to be serious
&lt;/h2&gt;

&lt;p&gt;If you follow the NBA you know my team, the Lakers, had a summer. LeBron declined to re-sign and is now a free agent. Ayton got traded for a bench guard and picks. In one 35-minute stretch of free agency, the front office signed four new players. It's Luka's team now, and every one of those bets rides on one question nobody can actually answer in July: do these guys fit the way Luka plays?&lt;/p&gt;

&lt;p&gt;My first idea was the obvious one. Build a metric. Score every player. Crown the offseason a success or a failure.&lt;/p&gt;

&lt;p&gt;Then I pressure-tested it in the Fable 5 chat, and the whole thing fell apart in the best way. Every fit metric is stuffed with opinions wearing a math costume. The weights are your bias. The samples are too small. The new guys have played zero possessions together, so any "chemistry" claim is fiction. A verdict engine was impossible to build honestly.&lt;/p&gt;

&lt;p&gt;So I flipped it. If the bias can't be removed, print it. The tool became a bias-visibility experiment disguised as a sports dashboard. Every assumption on the page, every sample size next to its score, and one hard rule that survived the entire build: no blended composite score, ever. Four axes stay four axes. If you want a single number that tells you what to think, this is the wrong tool on purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why usage, and the rules before the scores
&lt;/h2&gt;

&lt;p&gt;The whole thesis sits on one stat. Usage rate is the share of team possessions a player ends, a shot, free throws, or a turnover. It's the cleanest measurable answer to "who has the ball?" Luka just led the NBA at 38.0. He ends more possessions than anyone alive. So every teammate's fit starts with one question: can you be great without it?&lt;/p&gt;

&lt;p&gt;From there, four axes. Spacing, because his kick-outs need shooters. Play finishing, because efficiency without the ball is the job. Defensive cover, because someone has to guard so he doesn't. And ball-need, inverted, where lower usage scores higher next to the highest-usage player in basketball.&lt;/p&gt;

&lt;p&gt;The rules got decided before any player got scored, which mattered more than I expected. When the data later disagreed with my takes, the rules didn't bend. I did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fifteen-minute spike that earned its keep
&lt;/h2&gt;

&lt;p&gt;Before a line of the page existed, we ran my usual reality check, a fifteen-minute spike with pass/fail criteria. Fable 5 did the pulling: real stats for the four signings, proposed scores on each axis, receipts logged to a spike file I could verify against later. My job was the criteria and the verdict. The spike caught two things the concept phase, mine and the model's both, had confidently wrong.&lt;/p&gt;

&lt;p&gt;The 3&amp;amp;D wing the front office signed, Quentin Grimes, shot a career-worst 33.4% from three last season (no disrespect Quentin!). The story in my head was a season out of date. And the new starting center played five games before shoulder surgery ended his year, so his "best fit on paper" scores rest on old data and a repaired labrum.&lt;/p&gt;

&lt;p&gt;Two corrections in fifteen minutes, before a single pixel existed. That's when confidence tags got promoted from footnote to first-class UI element. They changed the verdict on half the players they touched.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question that broke my own metric
&lt;/h2&gt;

&lt;p&gt;Here's the part I want to remember. The tool flagged Reaves, our co-star, as the worst fit on the roster. His 26.6 usage next to Luka's 38.0 looked like pure overlap. The math was clean. The take was loud.&lt;/p&gt;

&lt;p&gt;Then I asked one question, and it wasn't a stats question, it was a fan question. What about the minutes Luka spends resting on the bench?&lt;/p&gt;

&lt;p&gt;Season usage can't tell the difference between a guard dominating the ball next to his star and a guard running the show while the star rests. Those are opposite signals wearing the same number. Fable 5 owned the miss immediately, then went and found game-level with/without splits hiding in plain sight on StatMuse. In the 41 games with Luka, Reaves ran a 25.2 usage. In the 10 games without him, 34.3, at a 67.5 true shooting. That's not a fit problem. That's your co-star proving he can carry the minutes the franchise player sits.&lt;/p&gt;

&lt;p&gt;The tool's loudest take collapsed under one good question, and the collapse became the best content on the page. There's a table on Reaves' card now showing all three numbers. The correction ships with the product.&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%2Fukjxidhfp75euhsx0dsf.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%2Fukjxidhfp75euhsx0dsf.png" alt=" " width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It kept happening, and I kept the receipts
&lt;/h2&gt;

&lt;p&gt;Correction two: the free agent board. The fit metric loved Gary Payton II, best defender available, and Fable 5 ranked him second. I asked whether forwards, wings, and centers should outrank guards on the priority list, since that felt like the actual hole. The model counted the roster, seven guards, three forwards, one and a half healthy centers, and demoted its own number two pick because he'd be the eighth guard. Fit and need are two lenses on the page now, shown separately, never averaged, because averaging away a real tension is how tools lie politely.&lt;/p&gt;

&lt;p&gt;Correction three: Nicolas Batum. He almost went unscored. Fable 5 had flagged him as the name most worth pulling, then neither of us pulled him. He surfaced when I asked for an audit of everything we'd discussed but not done, and the pull came back with the lowest usage of any player on the page, 9.7, with 40.4% shooting, at the exact position the roster lacks. The wing the need lens was begging for, nearly skipped. He's number two on the board with a note admitting it.&lt;/p&gt;

&lt;p&gt;And one more, the one I'm proudest of catching: the defensive scores originally leaned on steals, blocks, and reputation, which is a fraction of what defense is. Fable 5 and I worked through the upgrade together, the model documented it beautifully in the methodology, and then implemented it nowhere. I caught the gap by asking one question: wait, didn't we decide to change the defensive metrics? Fable 5 owned it, "you didn't miss anything, I did," pulled defensive rating for all fourteen scored players, and the signing marketed as a 3&amp;amp;D wing, Quentin again - apologies!,  graded out below team average. His cover score dropped from a 4 to a 2. Reputation lost to a number we could cite. That's the whole project in one sentence.&lt;/p&gt;

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

&lt;p&gt;The disclaimers are the product. Printing them turned out to be the more interesting design, and weirdly, the more credible one. Every "check my sources" claim on the page is one click to verify.&lt;/p&gt;

&lt;p&gt;Deterministic structure, labeled interpretation. The stats are pulled and printed. The 0 to 5 bars are my judgment mapping, and the page says so in the footer. The v1 PRD replaces my mapping with fixed thresholds, and its QA gate is brutal by design: where the formula disagrees with the scores, the formula wins.&lt;/p&gt;

&lt;p&gt;A tool that argues back is worth more than one that agrees. Three corrections, all documented, all still visible on the page. The build log reads like a transcript of me losing arguments to my own ideas and assumptions. I recommend it. &lt;/p&gt;

&lt;p&gt;And here's my Fable 5 review, since the title promised one: the hype for me wasn't speed or polish. It was that when my question broke its loudest take, it said so, pulled the data, and rewrote its own conclusion instead of defending it. That's the behavior I actually want from a build partner.&lt;/p&gt;

&lt;p&gt;The division of labor stayed clean, and that's why it worked. Fable 5 pulled every stat, drafted every score, built every page, and kept the receipts. I set the rules, asked the questions, caught the unimplemented decision, and approved everything that shipped. Creative director model. Neither half gets there alone.&lt;/p&gt;

&lt;p&gt;The best questions came from fandom, not from stats. "What about when Luka rests" is something any Lakers fan would ask. No model volunteered it. Same for "shouldn't wings and centers come first." The human in the loop, moi, earned their spot, and so did the model that took both questions seriously instead of defending its work.&lt;/p&gt;

&lt;p&gt;The page is designed to be provably wrong later. Every score can be graded against the 2026-27 season, and the footer promises it will be. An app that can audit its own past takes instead of just producing new ones forever. That's the honest version of sports analytics I can build from a laptop.&lt;/p&gt;

&lt;p&gt;It's live, it's free, and it will be stale within a week because the roster is still moving, which the page also tells you.&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%2F0ryqb6emev0yhrl26eo7.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%2F0ryqb6emev0yhrl26eo7.png" alt=" " width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Data and sources
&lt;/h2&gt;

&lt;p&gt;Everything on the page traces to public sources, and fair is fair, they did the real work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.statmuse.com/nba" rel="noopener noreferrer"&gt;StatMuse&lt;/a&gt; — every player stat on the page: usage rates, shooting splits, true shooting, defensive ratings, and the game-level with/without splits that changed the Reaves verdict. The underrated find of this build: StatMuse answers URL-pattern questions like a query API, no scraping required.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bleacherreport.com/articles/25450796-lakers-updated-roster-2026-nba-salary-cap-after-deandre-ayton-trade-free-agency" rel="noopener noreferrer"&gt;Bleacher Report&lt;/a&gt; — roster construction and cap position after the Ayton trade and the free agency signings.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.spotrac.com/nba/los-angeles-lakers/cap/_/year/2026" rel="noopener noreferrer"&gt;Spotrac&lt;/a&gt; — contracts and the 13-of-15 roster math.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nbcsports.com/nba/news/top-15-remaining-2026-nba-free-agents-jalen-duren-lebron-james-james-harden-still-available" rel="noopener noreferrer"&gt;NBC Sports&lt;/a&gt; — the remaining free agent market that fed the vet-minimum board.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.espn.com/nba/story/_/id/49239780/los-angeles-lakers-overall-biggest-questions-lebron-james-luka-doncic-walker-kessler-nba-free-agency-offseason" rel="noopener noreferrer"&gt;ESPN&lt;/a&gt; — the "35-minute flurry" reporting on the Lakers' free agency and offseason context.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nba.com/news/utah-jazz-walker-kessler-season-ending-shoulder-surgery" rel="noopener noreferrer"&gt;NBA.com&lt;/a&gt; — the Kessler shoulder surgery reporting behind his low-confidence tag.
The page itself carries the same credits with the same links, because a tool about visible assumptions should cite like it means it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://luka-fit-index.netlify.app/" rel="noopener noreferrer"&gt;https://luka-fit-index.netlify.app/&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;More of my work:&lt;/strong&gt; &lt;a href="https://earlgreyhot1701d.github.io/Clew-Labs/" rel="noopener noreferrer"&gt;https://earlgreyhot1701d.github.io/Clew-Labs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI Assisted. Human Approved. Powered by NLP.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>ai</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>AI For Fun! Électrique Chats for Hack the Kitty, Built with Kiro.</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Sat, 04 Jul 2026 03:14:14 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/ai-for-fun-electric-chats-at-hack-the-kitty-built-with-kiro-849</link>
      <guid>https://dev.to/earlgreyhot1701d/ai-for-fun-electric-chats-at-hack-the-kitty-built-with-kiro-849</guid>
      <description>&lt;h3&gt;
  
  
  A cat astrologer, spec-driven and running on Amazon Bedrock
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;A companion to &lt;a href="https://dev.to/earlgreyhot1701d/a-builder-in-paris-do-devs-dream-of-electrique-chats-3hd9"&gt;A Builder in Paris: Do Devs Dream of Électrique Chats?&lt;/a&gt;&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%2Fbl8vndsslf3on4cw5cp1.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%2Fbl8vndsslf3on4cw5cp1.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Last month I wrote about the idea. Six rainy days in Paris, a closed laptop, and a hackathon I did not mean to enter, and somewhere between the Musée de l'Orangerie and a lot of walking, an idea arrived. Cats are inscrutable. The people who love them are obsessed with understanding them anyway. Astrology is an old framework for making the unknowable feel readable, and maybe, just maybe, it helps us understand them a little. Her name is &lt;a href="https://dghcwayx8gb6b.cloudfront.net" rel="noopener noreferrer"&gt;Madame Minou&lt;/a&gt;, a French cat astrologer who reads your cat's stars from a café terrace.&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%2Fs0k211f58nj0n9tr5ghq.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%2Fs0k211f58nj0n9tr5ghq.png" alt=" " width="800" height="1224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That first article was the &lt;a href="https://dev.to/earlgreyhot1701d/a-builder-in-paris-do-devs-dream-of-electrique-chats-3hd9"&gt;idea&lt;/a&gt;. This one is the build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vibe-coded, but on rails
&lt;/h2&gt;

&lt;p&gt;Was it vibe-coded? You know it! AI wrote the lines, and I said "no, not like that" more times than I can count. But it was vibe-coding on rails, and the rails were Kiro. Before a single line of app code, I wrote the requirements in EARS notation, a design doc, and a build-ordered task list, all living in &lt;code&gt;.kiro/specs&lt;/code&gt;. Decide what "done" means before letting anyone, human or model, start building. The specs are what kept the vibes on track. &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%2Ffm4ivy64lb4tuutz5r9s.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%2Ffm4ivy64lb4tuutz5r9s.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then the steering files. &lt;code&gt;.kiro/steering&lt;/code&gt; held the enduring rules of the project: product principles, security guardrails, technical direction, and UI law. These were the thing that kept a long, multi-session build from drifting. When a new session opened, the steering files were already the shared context. "The café blue" was one token, not five guesses. Security was not optional. &lt;/p&gt;

&lt;p&gt;From there, the loop: Kiro implemented one approved block at a time, ran each task's PASS/FAIL QA gate on itself before moving to the next, and only stopped for my review on the two things that actually mattered. I directed and approved. Kiro proposed and built. Spec first, block by block, human in the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The facts are sacred
&lt;/h2&gt;

&lt;p&gt;Here is the part that looks like a party trick and isn't. Madame never guesses the chart. The sun sign is computed in code, deterministically. The model only writes the voice over the facts it is handed. It cannot invent a sign, because the facts come first. Deterministic structure, AI flavor. The astrology is the vocabulary; the structure underneath is the catnip. &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%2Fvf51g9xts2wpyt185smp.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%2Fvf51g9xts2wpyt185smp.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  All in on AWS
&lt;/h2&gt;

&lt;p&gt;Claude runs through Amazon Bedrock on IAM, which means there is not a single API key anywhere in the stack. Lambda, API Gateway, and DynamoDB run the readings and a real per-IP freemium gate. S3 and CloudFront serve the café. &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%2Fub2jno0ymm5zzc6m9vju.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%2Fub2jno0ymm5zzc6m9vju.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke during the build
&lt;/h2&gt;

&lt;p&gt;I promised myself limitations language would be a feature, so here is the struggle bus story, not the tidy one.&lt;/p&gt;

&lt;p&gt;I started on the Claude Platform on AWS path and hit a wall: my hackathon account could not complete the Marketplace subscription. So I pivoted to Amazon Bedrock, where my Claude access actually lived, and the whole thing got simpler.&lt;/p&gt;

&lt;p&gt;I wanted real ephemeris math for moon and rising signs, but &lt;code&gt;pyswisseph&lt;/code&gt; is a native C extension with no Python 3.13 wheel, and it would not compile in the build environment I had. Rather than fight a compiler at nine at night, I shipped a pure-Python sun sign (it is just a date-range lookup, no ephemeris required) and moved moon and rising to v2. Sun sign is most of the value, and now it is rock solid instead of theoretical.&lt;/p&gt;

&lt;p&gt;And the deploy. A reserved &lt;code&gt;AWS_REGION&lt;/code&gt; env var that failed the whole stack. A Lambda that returned "internal server error" because the build packaged the handler but not the server module it imported. CORS. A missing paragraph renderer. Every one of them a real bug, every one of them a one-line fix once I stopped guessing and read the actual error. Powered by NLP but humbled by CORS!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Moon and rising signs.&lt;/strong&gt; The pyswisseph build wall. A Lambda layer is the v2 fix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Daily nudge and history.&lt;/strong&gt; Shipped as honest "coming soon" stubs, not half-features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wider birth-city coverage, a premium tier, a custom domain, the exact Paris Métro font.&lt;/strong&gt; All real, all v2.
Shipped honest, not complete.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  For Penelope
&lt;/h2&gt;

&lt;p&gt;Madame Minou is for Penelope. Tuxedo, my wife's BFF and my fourteen-year frenemy, who I was allergic to the whole time and could barely pet. She passed in February. This is the first thing I have built for a cat I never quite got to hold. There is a quiet link in the app, in her memory, to the Lap of Love Angel Fund. Because the stars are just a beautiful vocabulary for love.&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%2Fvlm22l08l0ekl6vucp05.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%2Fvlm22l08l0ekl6vucp05.jpg" alt=" " width="800" height="1422"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Try Madame Minou: &lt;a href="https://dghcwayx8gb6b.cloudfront.net" rel="noopener noreferrer"&gt;https://dghcwayx8gb6b.cloudfront.net&lt;/a&gt;&lt;br&gt;
Code: &lt;a href="https://github.com/earlgreyhot1701D/madame-minou" rel="noopener noreferrer"&gt;https://github.com/earlgreyhot1701D/madame-minou&lt;/a&gt;&lt;br&gt;
Built for &lt;a href="https://hackthekitty.com/" rel="noopener noreferrer"&gt;Hack the Kitty&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI Assisted. Human Reviewed. Powered by NLP.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>kiro</category>
      <category>aws</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Can retrieval agents like ChatGPT and Perplexity read your website? Agentis Lux sees what they see.</title>
      <dc:creator>L. Cordero</dc:creator>
      <pubDate>Sun, 28 Jun 2026 21:13:08 +0000</pubDate>
      <link>https://dev.to/earlgreyhot1701d/can-retrieval-agents-like-chatgpt-and-perplexity-read-your-website-agentis-lux-sees-what-they-see-5cac</link>
      <guid>https://dev.to/earlgreyhot1701d/can-retrieval-agents-like-chatgpt-and-perplexity-read-your-website-agentis-lux-sees-what-they-see-5cac</guid>
      <description>&lt;p&gt;&lt;em&gt;I created &lt;a href="https://agentislux.io" rel="noopener noreferrer"&gt;Agentis Lux&lt;/a&gt; for the purposes of entering &lt;a href="https://h01.devpost.com/" rel="noopener noreferrer"&gt;H0 Hackathon&lt;/a&gt; (Vercel + AWS Databases). #H0Hackathon&lt;/em&gt; &lt;a href="https://devpost.com/software/agentis-lux-for-your-second-audience" rel="noopener noreferrer"&gt;See Agentis Lux's Devpost.com entry&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;It started with a comment at a hackathon.&lt;/p&gt;

&lt;p&gt;A you.com employee said the thing out loud: the web has a second audience now. When you ask ChatGPT or Perplexity a question, a retrieval agent fetches a page and reads its HTML to answer you. Not the laid-out site with the buttons and the hero image. The markup underneath. These agents arrive by the million, and many of them rely on the raw or minimally rendered HTML rather than running your JavaScript, so they often see far less of your page than a person does. &lt;/p&gt;

&lt;p&gt;That comment sent me to build. My first answer to it was &lt;a href="https://github.com/earlgreyhot1701D/hermes-clew" rel="noopener noreferrer"&gt;Hermes Clew&lt;/a&gt;, for the GitLab Duo Agent Platform Challenge. Hermes lived inside GitLab Duo Chat, no frontend, no database: a Python engine that scanned the HTML, JSX, and TSX files in a repo, scored them across six categories, and let an LLM reason over the findings. It proved the core idea. It also told developers how to fix things, lived inside one vendor's chat, and only worked on files in a repo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://agentislux.io" rel="noopener noreferrer"&gt;Agentis Lux&lt;/a&gt; is what happened when I took that idea to the open web and rebuilt it with a different stance. Any live URL, not just repo files. Its own product on a real cloud architecture, not a chat window. And no fix suggestions, on purpose, where Hermes used to hand them out. Same six-category bones, a new body, a sharper philosophy. It scans your site and shows you what that second audience experiences when it tries to read it.&lt;/p&gt;

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

&lt;p&gt;You paste a URL to &lt;a href="https://agentislux.io" rel="noopener noreferrer"&gt;Agentis Lux&lt;/a&gt;. You get a report. The report is written from the agent's point of view.&lt;/p&gt;

&lt;p&gt;Not "this is broken." More like: "an agent landing on this page can't tell which element starts checkout, because it's a styled div and not a button."&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%2Fp25fo9lbju0iq1ps6q17.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%2Fp25fo9lbju0iq1ps6q17.png" alt=" " width="800" height="855"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It reports findings. It does not suggest fixes, and that is on purpose. I know what the agent sees, not what you should change. That is the whole value: visibility, and you decide what to do with it. Awareness, not judgment.&lt;/p&gt;

&lt;p&gt;Six deterministic checks score the frontend out of 100: semantic HTML, form accessibility, ARIA, structured data, content in the HTML, and link and navigation. A parallel set of six API checks runs on the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one idea the architecture is built on
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/earlgreyhot1701D/perseus-clew/blob/main/docs/ARCHITECTURE.md" rel="noopener noreferrer"&gt;Structure is deterministic. Flavor is AI.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The checks and the scoring are pattern matching. No model touches the number. Same input, same score, every time. I only spend AI in two places where a regex can't help: a Bedrock call writes the one-line plain-language verdict, and a second Bedrock layer runs an agent simulation, reasoning about what a retrieval agent would experience on the page and reporting what it could and could not accomplish. Not an autonomous agent clicking around. A simulation of the experience.&lt;/p&gt;

&lt;p&gt;Vercel runs the entire frontend and the edge layer. The Next.js App Router app deploys to Vercel with the /api/scan route as a serverless proxy in front of the AWS backend, so the browser never talks to Lambda directly. Preview deployments on every push meant I could see each change live before it merged, which is most of how a solo builder keeps quality up without a QA team. The custom domain, HTTPS, and CDN were Vercel defaults I didn't have to think about, which kept my attention on the scan engine.&lt;/p&gt;

&lt;p&gt;The AI is constrained, not creative. Low temperature, capped tokens, and a system prompt that encodes the product's own rules: no fixes, no judgment words, no em dashes. The simulation returns structured JSON, and any finding it references is filtered against the deterministic findings, so the model can't invent something the math didn't catch. If it fails validation, it falls back to a template. Math for trust, and the AI is fenced into exactly the two jobs where judgment helps.&lt;/p&gt;

&lt;p&gt;Math stays math, so you can trust the number. Language and judgment are where AI earns its place.&lt;/p&gt;

&lt;p&gt;This sounds like a philosophy choice. It ended up being an economics choice that fell out of the architecture. The deterministic core runs at any scale for almost nothing, so the free tier can stay free. I only pay for model tokens on the sentence and the simulation, the two places a human reads. I didn't design that in a spreadsheet. It just dropped out of keeping the math and the AI in separate boxes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why DynamoDB, and how I used it
&lt;/h2&gt;

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

&lt;p&gt;The hackathon stack is Vercel on the frontend and AWS on the back, with DynamoDB as the data layer. I wanted to use DynamoDB as a deliberate data model, not a key-value afterthought, because every access pattern in this product is a single key lookup. That is exactly what it is built for.&lt;/p&gt;

&lt;p&gt;Five tables, each with one job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ScanCache&lt;/strong&gt;, 15-minute TTL, keyed by a hash of the URL, dedupes repeat fetches and keeps Bedrock cost down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ScanResults&lt;/strong&gt;, 24-hour TTL, keyed by an opaque id, anonymous, results that expire on their own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BenchmarkScans&lt;/strong&gt;, the 50-site dataset, with a GSI on vertical, rewritten monthly by an EventBridge refresh.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ScanCounters&lt;/strong&gt;, server-side counts, no PII. Reserved for the team tier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Users&lt;/strong&gt;, reserved for signed-in history. A stub.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two of those are live on every scan, one holds the benchmark, and two are reserved stubs for later. Two TTLs, two lifetimes, two reasons. Per-vertical rollups use the GSI, not a second database. No joins, no migrations, no idle server.&lt;/p&gt;

&lt;p&gt;The write on a live scan is fail-soft and async. The scan returns to you whether or not the write lands, and a failed write goes to CloudWatch instead of your screen. The scan result is the product. Persistence is a side effect.&lt;/p&gt;

&lt;p&gt;(The product is Agentis Lux. The engine is Perseus Clew, part of my Clew suite, which is why the AWS tables carry the PerseusClew prefix.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The bet I made in public
&lt;/h2&gt;

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

&lt;p&gt;Before the engine scanned anything, I wrote down what I expected it to find across 50 sites and committed it to the repo with a timestamp. &lt;a href="https://dev.to/earlgreyhot1701d/predictions-first-data-later-seven-hot-takes-on-ai-agent-readiness-before-i-scan-50-sites-599d"&gt;Predictions first&lt;/a&gt;, data later, so I couldn't move the goalposts.&lt;/p&gt;

&lt;p&gt;Then I scanned ten sites each across e-commerce, SaaS, content and media, US government, and indie builder projects.&lt;/p&gt;

&lt;p&gt;Indie builders won. Mean score 77 out of 100, ahead of government, SaaS, and e-commerce. The single highest score in the whole run was a personal developer portfolio at 91. Scores ran from 34 to 91. Four sites blocked the scan at the door, including OpenAI.&lt;/p&gt;

&lt;p&gt;I missed three of my six predictions. That is the point of pre-registering them. If I had gone six for six you should distrust me, because it would mean I only predicted what I already knew. The misses are where I learned something: that craft beats compliance, that the API is the real blind spot, and that a hand-built personal site reads cleaner to an agent than most of the web's biggest companies.&lt;/p&gt;

&lt;p&gt;The full dataset, including the sites that blocked me, is in &lt;a href="https://github.com/earlgreyhot1701D/perseus-clew/blob/main/docs/BENCHMARK-HYPOTHESES.md" rel="noopener noreferrer"&gt;the repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gaps
&lt;/h2&gt;

&lt;p&gt;Fetching arbitrary user-supplied URLs on a public endpoint is a security problem before it is a feature. The backend does full DNS resolution, blocks private and reserved IPs, validates every redirect hop, forces HTTPS, and caps size and time. That hardening took as long as some of the checks did.&lt;/p&gt;

&lt;p&gt;Bedrock had to be allowed to fail. If the model is slow or errors, the report still renders, because the AI verdict has a deterministic template under it as a floor. The hero line never breaks, because the score under it was never AI in the first place.&lt;/p&gt;

&lt;p&gt;And also: this is a solo build on a deadline. The backend is JavaScript, not TypeScript. The benchmark page serves a published snapshot instead of querying DynamoDB live. The results view still has heading-hierarchy work. All of it is written down in &lt;a href="https://github.com/earlgreyhot1701D/perseus-clew/blob/main/docs/KNOWN-LIMITATIONS.md" rel="noopener noreferrer"&gt;KNOWN-LIMITATIONS.md&lt;/a&gt;, as choices, with reasons. On a product whose whole thesis is readability, hiding the gaps would be the one move I could not make.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this sits next to the other tools
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.scrunchai.com" rel="noopener noreferrer"&gt;Scrunch&lt;/a&gt;, recently acquired by Sitecore, works on AI search visibility: whether your brand gets cited when someone asks an AI a question. That is about being found. Agentis Lux is about whether an agent can read and use what it finds. Visibility, not operability.&lt;/p&gt;

&lt;p&gt;Google's experimental &lt;a href="https://developer.chrome.com/docs/lighthouse/overview" rel="noopener noreferrer"&gt;Agentic Browsing audit in Lighthouse&lt;/a&gt; (May 2026) checks the agent-as-actor surface: WebMCP and whether a browser-driving agent can operate your page. Agentis Lux goes deeper on the agent-as-reader surface, the raw HTML a retrieval agent forms an impression from before it ever acts. Different door.&lt;/p&gt;

&lt;p&gt;The agentic web is new enough that Google only added experimental, unscored checks two months ago. That is not a reason this is unoriginal. It is evidence the lane is open.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the tool says about itself
&lt;/h2&gt;

&lt;p&gt;Agents are not one reader. They are a spectrum, from the retrieval crawler that never runs your JavaScript to the browser-driving agent that does. The interesting output is the gap between them, and that is where this goes next: live benchmark querying, score history, and a render mode that shows the delta between what a non-JS agent sees and what a JS-capable one sees.&lt;/p&gt;

&lt;p&gt;The tool scans its own site and publishes the result. It went from 70 to 96 after I fixed what it found, with one finding still open and shown anyway. Because if I scrubbed my own site to a perfect 100, you would have every reason not to trust the number on yours.&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%2Fdj8vt7j9zt5sbk56uo23.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%2Fdj8vt7j9zt5sbk56uo23.png" alt=" " width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try it on your own site: &lt;a href="https://agentislux.io" rel="noopener noreferrer"&gt;agentislux.io&lt;/a&gt;. The code, the methodology, and the raw benchmark data are in the &lt;a href="https://github.com/earlgreyhot1701D/perseus-clew" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For your second audience.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live: &lt;a href="https://agentislux.io" rel="noopener noreferrer"&gt;agentislux.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo video (2:57): &lt;a href="https://www.youtube.com/watch?v=bv56_XB1E_c" rel="noopener noreferrer"&gt;youtube.com/watch?v=bv56_XB1E_c&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Code (Perseus Clew engine): &lt;a href="https://github.com/earlgreyhot1701D/perseus-clew" rel="noopener noreferrer"&gt;github.com/earlgreyhot1701D/perseus-clew&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The earlier proof of concept, Hermes Clew: &lt;a href="https://github.com/earlgreyhot1701D/hermes-clew" rel="noopener noreferrer"&gt;github.com/earlgreyhot1701D/hermes-clew&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;H0 Hackathon: &lt;a href="https://h01.devpost.com/" rel="noopener noreferrer"&gt;h01.devpost.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - More from Clew Labs: &lt;a href="https://earlgreyhot1701d.github.io/Clew-Labs/" rel="noopener noreferrer"&gt;earlgreyhot1701d.github.io/Clew-Labs&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;AI assisted. Human approved. Powered by NLP.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>aws</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
