<?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: Rahul Vyas</title>
    <description>The latest articles on DEV Community by Rahul Vyas (@rahulvyas777).</description>
    <link>https://dev.to/rahulvyas777</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%2F4108839%2Ffc135095-7480-4710-ab5c-2cf8ca094076.jpg</url>
      <title>DEV Community: Rahul Vyas</title>
      <link>https://dev.to/rahulvyas777</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rahulvyas777"/>
    <language>en</language>
    <item>
      <title>The Pocket Retro: What a Companion App Really Costs</title>
      <dc:creator>Rahul Vyas</dc:creator>
      <pubDate>Fri, 25 Sep 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/rahulvyas777/the-pocket-retro-what-a-companion-app-really-costs-5ee9</link>
      <guid>https://dev.to/rahulvyas777/the-pocket-retro-what-a-companion-app-really-costs-5ee9</guid>
      <description>&lt;p&gt;Eleven parts ago, ClinicLive lived on a kiosk and a wall. This season made the trilogy's third promise: put it in the patient's pocket with AI assistance and hide nothing — every prompt verbatim, every native feature behind an interface each host answers honestly, every screenshot from a real emulator or a real desktop, every dead end kept in. The meter has stopped. Time to look at what we made, and at what we couldn't prove.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gallery
&lt;/h2&gt;

&lt;p&gt;This is the payoff post, so the pictures go first. The season's claim was &lt;em&gt;one set of screens, three hosts&lt;/em&gt;, so wherever the bench allowed it, a row is the same Razor component photographed on a phone, on a desk and in a browser.&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%2Fw7fuc8cbki5lbqqbo75s.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%2Fw7fuc8cbki5lbqqbo75s.png" alt="Home on the Android emulator: the 'Your visit, in your pocket' hero, a 'Find my appointment' card with a six-dot code field and a Show my visit button, four tabs along the bottom" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Phone&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%2Ffv2unxp74oo5n3653hc4.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%2Ffv2unxp74oo5n3653hc4.png" alt="Home in the Windows app: a dark 1100 by 760 window with a five-entry side rail on the left and the same hero and code card in a wider column" width="799" height="547"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Desk&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%2Fgn0i47gtbkvi71vu5eob.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%2Fgn0i47gtbkvi71vu5eob.png" alt="Home in a browser at phone width: the same hero and code card, light theme, bottom tab bar" width="375" height="812"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Browser&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Home. Part 2's phone and browser frames beside Part 10's desk frame — dark because the desktop's OS theme is, and the tokens follow it. Same component; only the width and the host's answers change.&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%2Fntxr1hjmqok115z29tmk.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%2Fntxr1hjmqok115z29tmk.png" alt="The Settings screen's About card on Android: Running on Android 13, Host Native app, Layout Phone (one hand, thumbs)" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Phone&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%2Fu2n2saz98zrg185ykvmg.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%2Fu2n2saz98zrg185ykvmg.png" alt="The Settings screen on Windows, dark theme, with a Nudges card and an About card: Running on WinUI 10.0.26200.9168, Host Native app, Layout Large screen" width="799" height="427"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Desk&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%2Fvoqjnkqqjhaekka0uydd.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%2Fvoqjnkqqjhaekka0uydd.png" alt="The Settings screen in a browser: Running on Web (server: Win32NT), Host Browser, Layout Large screen" width="375" height="812"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Browser&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The capability-interface pattern, photographed: one &lt;code&gt;Settings.razor&lt;/code&gt; asks &lt;code&gt;IPlatformInfo&lt;/code&gt; where it is and prints the answer. The Windows frame is from Part 5 — the phone-layout-on-a-monitor era — and it shows.&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%2Fi2t02jnfcjmpwln3l9em.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%2Fi2t02jnfcjmpwln3l9em.png" alt="The Waiting room page on Android: NOW SERVING Ava C., Up next 1 Noah B., five tabs at the bottom" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Phone&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%2Fnidglhgtbsyzodiyywrc.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%2Fnidglhgtbsyzodiyywrc.png" alt="The Waiting room page in the Windows window with the side rail, dark theme, Ava C. now serving and Noah B. up next" width="799" height="547"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Desk&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%2Fuc9m2z077fjz1orsoqlr.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%2Fuc9m2z077fjz1orsoqlr.png" alt="The Waiting room page in a browser at 1280 pixels, light theme, side rail on the left" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Browser&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The waiting room, Part 10: Ava C. being served, Noah B. next, on all three — the desk and the browser both wearing the rail that one 900-pixel media query hands to anything wide enough.&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%2Fugmzsvjxio52893hm0x8.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%2Fugmzsvjxio52893hm0x8.png" alt="My visit on Android after a phone check-in: Hi Maria, Checked in, a huge number 1, 'You're the only one waiting', and Part 3's Refresh button" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Phone&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%2F5phimvrzu3ukgfa5w1i2.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%2F5phimvrzu3ukgfa5w1i2.png" alt="The waiting-room TV board in its dark signage palette: UP NEXT 1 Maria G., clock reading 06:38" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The wall TV&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part 3's moment: Maria checked in from a phone and appeared on the TV built two seasons earlier, because the app's check-in is literally the kiosk's method. That Refresh button died in Part 4.&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%2Ftdgcnobl2tv7ur2phy7z.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%2Ftdgcnobl2tv7ur2phy7z.png" alt="Android's notification permission dialog, 'Allow ClinicLive Pocket to send you notifications?', over Liam's booked visit" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Part 5&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%2Fgis6ewu98j04cin6gmva.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%2Fgis6ewu98j04cin6gmva.png" alt="Android's location dialog with Precise and Approximate options over the Find us page" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Part 7&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%2Fwa6o1o728kw9bznzqsuc.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%2Fwa6o1o728kw9bznzqsuc.png" alt="Android's camera permission dialog, 'Allow ClinicLive Pocket to take pictures and record video?', over the Home screen" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Part 8&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Permission in context, three times: notifications at check-in, location on "Use my location", the camera on "Scan my ticket". Never at launch. The dialogs are Android's; the timing is ours.&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%2Fn72j77q56le8u8g9ckkk.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%2Fn72j77q56le8u8g9ckkk.png" alt="My visit for Emma with a coral 'reconnecting…' pill beside the heading and 'It's your turn — please go through' in the card" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before the fix&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%2Faub1t5jucx4pptdiirbk.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%2Faub1t5jucx4pptdiirbk.png" alt="The same screen for Emma with no pill: the server is back and the connection recovered" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part 4's evidence. Left: a full minute after the server came back, the default reconnect policy had given up for good. Right: the keep-trying policy — 2, 4, 8, 16 seconds, then every 30, never null. A browser tab gets refreshed; a phone in a car park doesn't.&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%2F1pbik5ua4na1ghfnmi5m.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%2F1pbik5ua4na1ghfnmi5m.png" alt="The clinic website's booking ticket for Leo: Thursday 3 September at 15:30, the code 5GYZRH in large letters, a QR code on a white tile and copy inviting a scan with the Pocket app" width="375" height="812"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The ticket&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%2Fjevwhdqsis8vp1cwnb1n.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%2Fjevwhdqsis8vp1cwnb1n.png" alt="The native scanner page on the emulator: a live camera view of the emulator's virtual room — a bookshelf, a low-poly cat, a TV showing a checkerboard — with 'Point the camera at the QR on your ticket' and a Cancel button" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The scanner&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part 8: a QR on the ticket and a native ZXing page pushed over the Blazor WebView. The camera is looking at the emulator's virtual living room, not at a ticket — more on that below.&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%2Fpw8m0ang5p4b474rxlbl.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%2Fpw8m0ang5p4b474rxlbl.png" alt="Airplane mode: an offline strip under the header, a notice 'Showing what the clinic said at 13:34 — we can't reach it right now', Leo's cached visit and a disabled check-in button" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Offline&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%2Floxk2q87rzsomhizkmf8.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%2Floxk2q87rzsomhizkmf8.png" alt="Signal back: the same visit for Leo with no strip, no notice, and the check-in button enabled" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Back online&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part 9: a code in the keystore, a queue state cached with the time it was true. Stale means read-only — you can see the button but you can't press it.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The bill, honestly
&lt;/h2&gt;

&lt;p&gt;Same rules as &lt;a href="https://www.coder000.com/post/ai-development-cost-retrospective" rel="noopener noreferrer"&gt;season one's retro&lt;/a&gt; and &lt;a href="https://www.coder000.com/post/ai-ui-design-retrospective" rel="noopener noreferrer"&gt;season two's&lt;/a&gt;: cumulative API spend from the build log as each part landed. Part 1 was the plan, with no commit, so it rides inside Part 2's reading:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What each part of the companion app cost in API spend (cumulative)&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What we built&lt;/th&gt;
&lt;th&gt;Running total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-companion-app-plan" rel="noopener noreferrer"&gt;1&lt;/a&gt;–&lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-shared-ui-three-hosts" rel="noopener noreferrer"&gt;2&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;The plan; contracts, the shared RCL, two hosts, the first capability; Android's insets&lt;/td&gt;
&lt;td&gt;≈ $1.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-app-api-for-blazor-server-backend" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/api/pocket&lt;/code&gt;, the typed client, the real Visit screen&lt;/td&gt;
&lt;td&gt;≈ $1.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/signalr-in-maui-blazor-hybrid" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The phone joins the hub; lifecycle; the reconnect that never gives up&lt;/td&gt;
&lt;td&gt;≈ $2.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-haptics-local-notifications" rel="noopener noreferrer"&gt;5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Two verbs of haptics, notifications in context — and the Windows dead end&lt;/td&gt;
&lt;td&gt;≈ $3.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/firebase-push-notifications-maui-android" rel="noopener noreferrer"&gt;6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Firebase push to a closed app; the stopped-state lesson&lt;/td&gt;
&lt;td&gt;≈ $4.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-geolocation-maps" rel="noopener noreferrer"&gt;7&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Geolocation, a testable haversine, package visibility&lt;/td&gt;
&lt;td&gt;≈ $4.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-qr-scanner-zxing-blazor-hybrid" rel="noopener noreferrer"&gt;8&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;QR on the ticket, the native scanner — and the poster hunt&lt;/td&gt;
&lt;td&gt;≈ $5.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-offline-preferences-securestorage" rel="noopener noreferrer"&gt;9&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Connectivity, two-tier storage, the remembered and cached visit&lt;/td&gt;
&lt;td&gt;≈ $6.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-windows-desktop" rel="noopener noreferrer"&gt;10&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A desk-sized window, one breakpoint, the waiting-room page&lt;/td&gt;
&lt;td&gt;≈ $6.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-release-build-signing-ci" rel="noopener noreferrer"&gt;11&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Signed Android, self-contained Windows, CI, the runbook&lt;/td&gt;
&lt;td&gt;≈ $7.20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💸 &lt;strong&gt;The meter, final reading:&lt;/strong&gt; ≈ &lt;strong&gt;$7.20&lt;/strong&gt; for the whole companion app — nine capability interfaces, three hosts, real push, real haptics, a real scanner, offline, a signed build and CI. Season one's &lt;a href="https://www.coder000.com/post/ai-development-cost-retrospective" rel="noopener noreferrer"&gt;$6.20&lt;/a&gt; built ClinicLive; season two's &lt;a href="https://www.coder000.com/post/ai-ui-design-retrospective" rel="noopener noreferrer"&gt;$3.60&lt;/a&gt; designed it; $7.20 put it in a pocket. Seventeen dollars for the trilogy. The two big dead ends — Part 5's toast chase and Part 8's poster hunt — are in that number; everything else was proportionate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;How the meter is measured: the build's actual token consumption priced at the API list rates of the models used (the 🧠 boxes name them). On a subscription plan your bill won't literally say $7.20 — but the tokens burned are the same, so it's the honest apples-to-apples number.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And the caveat, one last time, because it was truer this season than either before: the dollars are not the real cost. The real cost was the bench — a GPS fix to fake, airplane mode to toggle, a vibrator log to read, a Firebase console to watch, a desktop to photograph without photographing anything else. The meter bought code; the proving is where the season went.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complete mistake log
&lt;/h2&gt;

&lt;p&gt;Every 🤖 from eleven parts in one table, including two that were about the process, not the code:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What the AI got wrong, part by part&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What went wrong&lt;/th&gt;
&lt;th&gt;Caught by&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 — setup&lt;/td&gt;
&lt;td&gt;The Windows target failed with MSB3030 copying a Windows App SDK DLL. The scratch path was about 270 characters; the App SDK's copy step dies silently past MAX_PATH. Ten seconds to build at a short path.&lt;/td&gt;
&lt;td&gt;A shorter folder name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 — hosts&lt;/td&gt;
&lt;td&gt;A purple status bar: the template's &lt;code&gt;colors.xml&lt;/code&gt; still painted it. CSS tokens don't reach Android's window chrome.&lt;/td&gt;
&lt;td&gt;The first Android screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 — hosts&lt;/td&gt;
&lt;td&gt;Header and tab labels missing on Android: the WebView draws under the system bars and &lt;code&gt;env(safe-area-inset-*)&lt;/code&gt; reads 0 inside it. Fixed natively with an insets listener.&lt;/td&gt;
&lt;td&gt;The Android shot beside the Windows one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 — hosts&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;View&lt;/code&gt; is ambiguous inside a MAUI project.&lt;/td&gt;
&lt;td&gt;The compiler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 — hosts&lt;/td&gt;
&lt;td&gt;The h1 focus ring was back on Windows — &lt;code&gt;FocusOnNavigate&lt;/code&gt;'s &lt;code&gt;tabindex="-1"&lt;/code&gt; matched the ring rule. Season two's bug, new face.&lt;/td&gt;
&lt;td&gt;The Windows screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 — hosts&lt;/td&gt;
&lt;td&gt;The web host served every static file empty: no environment set means Production, and &lt;code&gt;MapStaticAssets&lt;/code&gt; wanted published assets.&lt;/td&gt;
&lt;td&gt;A screenshot that looked like 1996&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 — hosts&lt;/td&gt;
&lt;td&gt;The harness passed &lt;code&gt;-nologo&lt;/code&gt; into the app's own arguments; Chromium screenshotted a directory listing.&lt;/td&gt;
&lt;td&gt;The screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 — hosts&lt;/td&gt;
&lt;td&gt;The Windows window opened maximized with the phone layout stretched across a 4K monitor. Deferred to Part 10.&lt;/td&gt;
&lt;td&gt;The screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 — API&lt;/td&gt;
&lt;td&gt;The "tomorrow" test booked the slot season one's booking test owns in the shared fixture; the &lt;em&gt;old&lt;/em&gt; test went red.&lt;/td&gt;
&lt;td&gt;The old test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 — API&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;postgres:18&lt;/code&gt; images refuse a volume at the old data path; compose stopped working.&lt;/td&gt;
&lt;td&gt;Compose failing to start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 — API&lt;/td&gt;
&lt;td&gt;Builds failed on DLL locks while the hosts were still running.&lt;/td&gt;
&lt;td&gt;The build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 — API&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;--no-launch-profile&lt;/code&gt; means Production — for the server, no demo seed.&lt;/td&gt;
&lt;td&gt;An empty API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 — live&lt;/td&gt;
&lt;td&gt;Cold start grew from about 4 s to 7.5 s with the SignalR client; adb taps at four seconds were dropped. A slow app looked like a broken one.&lt;/td&gt;
&lt;td&gt;logcat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 — live&lt;/td&gt;
&lt;td&gt;The emulator dimmed for inactivity. Gray screenshots, no bug.&lt;/td&gt;
&lt;td&gt;A gray screenshot, then skepticism&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 — live&lt;/td&gt;
&lt;td&gt;"Now serving David C.." — the masked name already ends with a full stop.&lt;/td&gt;
&lt;td&gt;The screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 — live&lt;/td&gt;
&lt;td&gt;Part 2's insets code left two warnings.&lt;/td&gt;
&lt;td&gt;The compiler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 — live&lt;/td&gt;
&lt;td&gt;A minute after the server came back, the phone still said "reconnecting…": &lt;code&gt;WithAutomaticReconnect()&lt;/code&gt; gives up after four tries, forever. Replaced with a keep-trying policy.&lt;/td&gt;
&lt;td&gt;The screenshot a minute after restart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 — nudges&lt;/td&gt;
&lt;td&gt;Windows toasts never displayed: registration OK, setting Enabled, &lt;code&gt;Show()&lt;/code&gt; returned, no banner — even a PowerShell toast to that identity was silent. Open all season.&lt;/td&gt;
&lt;td&gt;The "Allowed, nothing shown" screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 — nudges&lt;/td&gt;
&lt;td&gt;Windows reported haptics "available" — &lt;code&gt;IsSupported&lt;/code&gt; is optimistic on a desktop. Cosmetic.&lt;/td&gt;
&lt;td&gt;The Settings screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 — process&lt;/td&gt;
&lt;td&gt;The first full-screen capture photographed the &lt;em&gt;entire desktop&lt;/em&gt;, every other window included. Deleted before any commit; the harness now refuses full-screen unless the app is maximized, and excludes the taskbar.&lt;/td&gt;
&lt;td&gt;A human, looking before committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 — process&lt;/td&gt;
&lt;td&gt;A "diagnostic" Notepad the harness opened was a tab in the user's own Notepad window — Windows 11 Notepad is single-instance — and closing it closed theirs. Never automate other people's apps.&lt;/td&gt;
&lt;td&gt;The person whose window it was&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6 — push&lt;/td&gt;
&lt;td&gt;D8: &lt;code&gt;FragmentKt&lt;/code&gt; defined twice — Firebase's fragment-ktx 1.8.8 against MAUI 10's 1.9.0. Pinned.&lt;/td&gt;
&lt;td&gt;The build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6 — push&lt;/td&gt;
&lt;td&gt;"App closed" was simulated with &lt;code&gt;am force-stop&lt;/code&gt;: Firebase said 1 delivered, the phone showed nothing. Force-stop is Android's stopped state, where push is never delivered; swiping an app away isn't. Redone with HOME plus &lt;code&gt;am kill&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;A phone showing nothing after "1 delivered"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6 — push&lt;/td&gt;
&lt;td&gt;"Push is on" missing from the first shot — the token round-trip takes seconds. Not a bug.&lt;/td&gt;
&lt;td&gt;An early screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6 — push&lt;/td&gt;
&lt;td&gt;Two identical "You're next": the check-in reload and the hub reload raced. Loads serialized.&lt;/td&gt;
&lt;td&gt;The notification shade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6 — push&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;IsInForeground&lt;/code&gt; defaulted to true; a process Firebase starts has no window at all.&lt;/td&gt;
&lt;td&gt;Reading the closed-app path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 — location&lt;/td&gt;
&lt;td&gt;"No maps app" with Google Maps installed: Android 11's package visibility needs a &lt;code&gt;&amp;lt;queries&amp;gt;&lt;/code&gt; block in the manifest.&lt;/td&gt;
&lt;td&gt;The screenshot, with Maps right there&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 — location&lt;/td&gt;
&lt;td&gt;Maps then launched and crashed — the emulator's stale build. The intent resolved; the crash was photographed, not hidden.&lt;/td&gt;
&lt;td&gt;The emulator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 — location&lt;/td&gt;
&lt;td&gt;The test asserted Greenwich to the Eiffel Tower is about 341 km; the haversine's 333.8 was right. The expectation was fixed, not the formula.&lt;/td&gt;
&lt;td&gt;A failing test, checked by hand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 — location&lt;/td&gt;
&lt;td&gt;Test builds blocked by the running server's DLL lock.&lt;/td&gt;
&lt;td&gt;The build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 — scanner&lt;/td&gt;
&lt;td&gt;The emulator never rendered its QR posters — forty minutes sweeping the room. A limit of the bench; the decode is proven by a test instead.&lt;/td&gt;
&lt;td&gt;Forty minutes of looking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 — scanner&lt;/td&gt;
&lt;td&gt;The first web ticket shot was mid-animation, QR not yet loaded. The harness now waits.&lt;/td&gt;
&lt;td&gt;The screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 — scanner&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.CacheOutput()&lt;/code&gt; without output-cache middleware — swapped for a Cache-Control header before it could throw.&lt;/td&gt;
&lt;td&gt;Review, before runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 — scanner&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;[A-HJ-NP-Z2-9]&lt;/code&gt; let the banned L back in — J to N spans J, K, L, M, N — so a cereal-box URL parsed as ticket CEREAL.&lt;/td&gt;
&lt;td&gt;The new test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 — scanner&lt;/td&gt;
&lt;td&gt;ZXing.Net and ZXing.ImageSharp both define &lt;code&gt;BarcodeReader&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;The compiler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9 — offline&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SecureStorage.GetAsync&lt;/code&gt; throws after a keystore reset; Home would have crashed on a restored phone.&lt;/td&gt;
&lt;td&gt;Reading the restore path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9 — offline&lt;/td&gt;
&lt;td&gt;Storage read in &lt;code&gt;OnInitialized&lt;/code&gt; — no browser under the web host's prerender. Moved to &lt;code&gt;OnAfterRender&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;The web host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9 — offline&lt;/td&gt;
&lt;td&gt;The native checkbox was Android-blue against petrol. One &lt;code&gt;accent-color&lt;/code&gt; line.&lt;/td&gt;
&lt;td&gt;The Android screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 — desk&lt;/td&gt;
&lt;td&gt;No code catch. The honest entry is what stayed unfixed — toasts, because the answer is identity, not C#.&lt;/td&gt;
&lt;td&gt;Named, not fixed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 — process&lt;/td&gt;
&lt;td&gt;The narrow-window frame was re-shot later, and the file that came back was not the app: Windows had refused the script's request to bring the window forward, so the capture copied whatever was in front of it — an unrelated window. Deleted before any commit; the harness now refuses to shoot unless the app window is in front and owns every corner of the frame.&lt;/td&gt;
&lt;td&gt;A human, looking at the image before writing its caption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11 — ship&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-r win-x64&lt;/code&gt; makes NuGet restore every target for that runtime, and Android's Mono runtime has no win-x64 pack (NU1102). Drop the flag.&lt;/td&gt;
&lt;td&gt;The restore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11 — ship&lt;/td&gt;
&lt;td&gt;The MSIX build reported success and produced no sideloadable package outside &lt;code&gt;obj/&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Looking in the output folder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11 — ship&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;apksigner.bat&lt;/code&gt; exits non-zero after printing the certificates; the harness read "failed" for a success.&lt;/td&gt;
&lt;td&gt;Reading the output, not the exit code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11 — ship (after publishing)&lt;/td&gt;
&lt;td&gt;The workflow's first real run failed its Android job with &lt;code&gt;NETSDK1147&lt;/code&gt;: it installed the &lt;code&gt;android&lt;/code&gt; workload, and a MAUI project needs &lt;code&gt;maui-android&lt;/code&gt;. One word, fixed on top of the tag; the re-run passed on all three targets.&lt;/td&gt;
&lt;td&gt;The first real CI run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the right-hand column and a third pattern appears. Season one's mistakes were about &lt;em&gt;context&lt;/em&gt; — your rules, your timezone — and were caught by reading code against a spec. Season two's were about &lt;em&gt;looking&lt;/em&gt;, and the screenshot loop caught them. This season's were about &lt;strong&gt;the platform underneath the code&lt;/strong&gt;: insets, stopped states, package visibility, MAX_PATH, restore semantics, exit codes. Almost none are visible in a diff, and a screenshot alone often wasn't enough either — the catch needed a screenshot &lt;em&gt;plus&lt;/em&gt; a skeptic: a gray frame that was only a dimmed emulator, a "1 delivered" that meant nothing, an exit code that lied in both directions. The two process rows cost no tokens and taught the most: a screenshot harness needs a privacy guard, not just a crop, and it must never touch a window it didn't open.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the AI never caught alone
&lt;/h2&gt;

&lt;p&gt;The table undersells the human half again, because the biggest contributions were &lt;em&gt;decisions&lt;/em&gt;, and this season they lived in the prompts. Two verbs of haptics, because an app that vibrates for everything gets muted. Permission at check-in, never at launch. "A second front door must not grow a second set of rules." "The code is the credential." "Stale means read-only." "Prove as much as the bench allows" and its twin, "fake nothing." Each was a sentence a person wrote before the model started, and each would have shipped as something plausible and worse without it. The two process incidents were human catches too: the model doesn't know what else is on your desktop, or whose Notepad that is. And Part 7's wrong "known distance" was caught by a test the model wrote but &lt;em&gt;decided&lt;/em&gt; by a human working the numbers by hand — a failing test is a question, not an order.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🤖 &lt;strong&gt;What the AI got wrong, the season in one sentence:&lt;/strong&gt; it knew the APIs and not the platform. It wrote &lt;code&gt;NotificationCompat&lt;/code&gt; from memory and didn't know a force-stopped app never receives a push; it wired a ZXing camera view in an afternoon and didn't know the emulator couldn't show it a poster; it signed an APK and didn't know &lt;code&gt;apksigner&lt;/code&gt; exits non-zero on success. Platform knowledge only comes from being bitten — and a bench, a log and a skeptic are how you get bitten on purpose, cheaply, before a patient does.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What the AI was brilliant at
&lt;/h2&gt;

&lt;p&gt;Fairness cuts both ways, and this season's wins were the biggest of the three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The capability-interface pattern, nine times over.&lt;/strong&gt; From &lt;code&gt;IPlatformInfo&lt;/code&gt; to &lt;code&gt;IAppStorage&lt;/code&gt;, each a small interface in the shared project, answered by MAUI next to the platform it talks to and by the web host honestly — &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, "not here". Introduced once in Part 2, never bent, so the screens never learned which host they were on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diagnoses that would have cost a person a day.&lt;/strong&gt; Safe-area insets reading zero inside a WebView. A 270-character path killing a Windows build. Android 11's package visibility. The AndroidX version dance. Each named from a screenshot or a build log of the symptom.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proof by other means.&lt;/strong&gt; When the emulator couldn't be shown a QR, it proposed the test that decodes the server's PNG back through the parser — the whole chain except the lens. When no screenshot can show a vibration, it went to the OS's vibrator log: 182 ms, 102 ms, 403 ms. It looked for evidence instead of settling for a claim.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honest no-ops.&lt;/strong&gt; "iOS: not built here." "Windows: no push." "Browser: not a scanner." The season's least glamorous code and its most trustworthy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The runbook.&lt;/strong&gt; &lt;code&gt;docs/pocket.md&lt;/code&gt;, written in the same session as the code it describes — the only time anyone ever writes one.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Model pick, the season's rule:&lt;/strong&gt; Opus wherever the question was about &lt;em&gt;semantics&lt;/em&gt; — foreground versus background versus stopped, what "resumed" must mean for a socket, whether stale data is read-only, what not to fake; Sonnet wherever Essentials or a documented toolchain did the heavy lifting — geolocation, storage, CSS, signing, YAML. Same principle as the two seasons before, on a new axis: in a mobile app, the consequence of a cheap-model mistake is a notification that never arrives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The three things the bench couldn't prove
&lt;/h2&gt;

&lt;p&gt;Every part photographed what it claimed, and three claims could not be photographed. They belong together, so nobody has to hunt for them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Windows toast notifications never displayed.&lt;/strong&gt; Not in Part 5, not in Part 10, not from Part 11's published build. Registration succeeds, the OS setting says Enabled, &lt;code&gt;Show()&lt;/code&gt; returns, and no banner appears. The cause is app identity — the unpackaged build has none — and the fix is MSIX packaging or an installer-made shortcut with an explicit AppUserModelID, neither of which this series demonstrates, because sideloading an MSIX means trusting a home-made certificate. The Windows app says "Allowed" and shows nothing. That's the honest state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No on-device QR decode was photographed.&lt;/strong&gt; The emulator never rendered its virtual-scene posters, so the scanner was only ever pointed at a virtual living room. The chain — the server's PNG, read by ZXing back to &lt;code&gt;cliniclive://visit/CODE&lt;/code&gt;, through the parser — is proven by &lt;code&gt;TicketQrTests&lt;/code&gt;, which runs in CI. Everything except the lens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Maps crashed on the emulator.&lt;/strong&gt; After the package-visibility fix the directions intent resolved and Maps launched — then died, the emulator's stale Maps build without a Play sign-in. What we control worked; what a real phone does next was not shown.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg2u9hz27ypbwqm6x6xi5.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%2Fg2u9hz27ypbwqm6x6xi5.png" alt="The Windows app maximized, dark theme, on the Settings page: Haptics 'Available on this device', Notifications 'Allowed', the three test buttons — and no toast banner anywhere on the screen" width="800" height="438"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Windows: Allowed, nothing shown&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%2F9cgnnagaojynxd1if1q8.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%2F9cgnnagaojynxd1if1q8.png" alt="An Android system dialog reading 'Maps keeps stopping' with App info and Close app options, over the Find us page" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The emulator's Maps&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Two of the three, exactly as they happened. Left: the season's only full-screen Windows capture — taken with the app maximized so nothing else could be in frame — and the banner that isn't in it. Right: Maps, resolved and crashed. The third has no picture, which is rather the point.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Was Blazor Hybrid the right bet?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-companion-app-plan" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt; made the case before any code existed: for a companion to an existing Blazor product, the screens would be the cheap part and the platform edges the whole cost, so sharing the screens would pay for itself. The retro gets to check it.&lt;/p&gt;

&lt;p&gt;The screens were cheaper than cheap. Five pages written once; the Settings triplet above is the proof they never learned where they were. Season two's tokens carried straight over, dark mode included. The waiting-room page was forty lines because the live plumbing already existed. And the party trick paid off exactly where it mattered: a native camera page pushed over the WebView, because the WebView is just one page in a native app.&lt;/p&gt;

&lt;p&gt;The edges cost what edges cost, and the table above is that bill: insets CSS can't see, a status bar the tokens can't reach, a cold start three and a half seconds slower with the SignalR client, a 33.8 MB APK and a 235 MB Windows folder, the AndroidX version dance, toasts that need an identity, iOS compiling but never built for want of a Mac. None of that is Blazor's fault — and, the honest point, &lt;em&gt;none of it would have been avoided by going native&lt;/em&gt;. A Kotlin app meets package visibility and the stopped state too; a WinUI app meets the same identity rule. Native would have paid the same edge bill &lt;em&gt;and&lt;/em&gt; rebuilt the screens, three times.&lt;/p&gt;

&lt;p&gt;So: yes, for this app — a small team, an existing Blazor product, screens that are forms, cards and one big number. Where the bet goes the other way: an app whose &lt;em&gt;product&lt;/em&gt; is the camera, or gesture-heavy motion, or platform idioms polished enough to sell a store listing. There the WebView is the wrong stage and shared screens the wrong savings. Know which app you have before you place it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tests postscript
&lt;/h2&gt;

&lt;p&gt;The number that grew all season: 9 tests from season one, 13 after &lt;a href="https://www.coder000.com/post/maui-app-api-for-blazor-server-backend" rel="noopener noreferrer"&gt;Part 3&lt;/a&gt;'s API tests against a real Postgres, 15 after &lt;a href="https://www.coder000.com/post/firebase-push-notifications-maui-android" rel="noopener noreferrer"&gt;Part 6&lt;/a&gt;'s push test with a fake sender, 20 after &lt;a href="https://www.coder000.com/post/maui-geolocation-maps" rel="noopener noreferrer"&gt;Part 7&lt;/a&gt;'s haversine, 28 after &lt;a href="https://www.coder000.com/post/maui-qr-scanner-zxing-blazor-hybrid" rel="noopener noreferrer"&gt;Part 8&lt;/a&gt;'s QR round-trip and parser — and &lt;strong&gt;28 green&lt;/strong&gt; through Parts 9, 10 and 11, which changed nothing the tests can see. Two of them caught the AI red-handed: the wrong "known distance" and the regex that let a banned letter back in. The &lt;a href="https://www.coder000.com/post/testing-ai-generated-code" rel="noopener noreferrer"&gt;harness built in season one&lt;/a&gt; is now what lets a phone, a desk and a browser share one set of rules without anyone re-checking them by hand. Three seasons in, the compounding return on those first nine tests is the least surprising and most useful fact in the trilogy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Checkpoint, the last one:&lt;/strong&gt; you've now shipped a companion app, not a demo of one — shared screens on three hosts, nine capabilities answered honestly, a live queue, push to a closed phone, haptics proven by the kernel, a QR pipeline proven by a test, offline that tells the truth about its age, a signed build and CI. And you know exactly which three things it can't yet prove. Here's what's next: pick one screen of an app you own, put its native needs behind a capability interface, and run it in a second host. You'll meet your first edge within the hour — write it down.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where next 🎉
&lt;/h2&gt;

&lt;p&gt;Twelve parts, one companion app, $7.20. The whole three-season arc — every prompt, every tag, every honest screenshot — lives in &lt;a href="https://github.com/rahulvyas777/clinic-live" rel="noopener noreferrer"&gt;the ClinicLive repo&lt;/a&gt;; the README's season-three table maps &lt;code&gt;pocket-02&lt;/code&gt; through &lt;code&gt;pocket-11&lt;/code&gt; to their parts, and &lt;code&gt;docs/pocket.md&lt;/code&gt; takes you from a fresh clone to a signed APK.&lt;/p&gt;

&lt;p&gt;If you arrived through the pocket door, the story starts two seasons earlier: &lt;a href="https://www.coder000.com/post/ai-assisted-development-with-claude" rel="noopener noreferrer"&gt;From Prompt to Production, Part 1&lt;/a&gt; builds ClinicLive from a spec to a VPS, and &lt;a href="https://www.coder000.com/post/ai-ui-design-escaping-the-slop" rel="noopener noreferrer"&gt;From Prompt to Polish, Part 1&lt;/a&gt; makes it something people would want to use. The full catalog of everything on this site is on the &lt;a href="https://www.coder000.com/series" rel="noopener noreferrer"&gt;series page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And that's the trilogy complete. Season one proved AI can build software with you. Season two proved it can design with you, steered. This season proved it can reach the parts of a product that live on someone else's operating system — and that the steering matters &lt;em&gt;more&lt;/em&gt; there, not less, because the mistakes are no longer in the code where a diff can show them. A prompt that says what not to fake. A bench that toggles airplane mode. A log that keeps the dead ends next to the wins. A skeptic reading the exit codes. The tools got the app onto the phone; the honesty is what makes it worth trusting there.&lt;/p&gt;

&lt;p&gt;Thanks for coming all the way — three seasons, thirty-six parts, one clinic. If any of it helped you ship something, or you hit an edge this series didn't, the &lt;a href="https://www.coder000.com/contact" rel="noopener noreferrer"&gt;contact page&lt;/a&gt; is open and I'd genuinely like to hear about it. Now go put something of yours in someone's pocket. 📱&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-retrospective" rel="noopener noreferrer"&gt;coder000.com&lt;/a&gt;. Part of the series &lt;a href="https://www.coder000.com/series/from-prompt-to-pocket" rel="noopener noreferrer"&gt;From Prompt to Pocket&lt;/a&gt; — read the whole series there.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>maui</category>
      <category>mobile</category>
      <category>ai</category>
    </item>
    <item>
      <title>From Prompt to Pocket: One App for Phone, Desk and Browser</title>
      <dc:creator>Rahul Vyas</dc:creator>
      <pubDate>Mon, 21 Sep 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/rahulvyas777/from-prompt-to-pocket-one-app-for-phone-desk-and-browser-107m</link>
      <guid>https://dev.to/rahulvyas777/from-prompt-to-pocket-one-app-for-phone-desk-and-browser-107m</guid>
      <description>&lt;p&gt;Season one, &lt;a href="https://www.coder000.com/post/ai-assisted-development-with-claude" rel="noopener noreferrer"&gt;From Prompt to Production&lt;/a&gt;, built ClinicLive: a clinic books appointments, checks patients in at a kiosk, and calls them from a live waiting-room board. Season two, &lt;a href="https://www.coder000.com/post/ai-ui-design-escaping-the-slop" rel="noopener noreferrer"&gt;From Prompt to Polish&lt;/a&gt;, made it look like someone meant it. Both seasons ended with the same person in the same chair — the patient, staring at a TV on a wall. This third and final season puts the queue in their pocket: one set of Blazor screens for an Android phone, a Windows desk and a browser, built with the same AI pair, twelve parts, every dead end kept in.&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%2Fie3lxj9b19pnaxroqm2m.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%2Fie3lxj9b19pnaxroqm2m.png" alt="ClinicLive Pocket's Home screen on an Android phone, light theme: the headline 'Your visit, in your pocket.', a 'Welcome back' card with a 'Continue with 5GYZRH' button, a second card for a different appointment with a six-dot code field, 'Show my visit' and a 'Scan my ticket' button, and four tabs along the bottom." width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Android (Part 9)&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%2Ffv2unxp74oo5n3653hc4.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%2Ffv2unxp74oo5n3653hc4.png" alt="The same Home screen as a Windows desktop app, dark theme: a side rail on the left with Home, My visit, Waiting room, Find us and Settings, and the 'Find my appointment' card in a wide content column." width="799" height="547"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Windows (Part 10)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Where the season ends, shown first so you know what you're building: one Home component, rendered by the Android emulator in Part 9 (light theme, four tabs, a remembered visit) and by the Windows app in Part 10 (dark theme, the side rail, five tabs — the Waiting room arrives that part). Each app follows its host's theme. The white area to the right of and below the Windows frame is the capture, not the app.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why the patient deserves an app
&lt;/h2&gt;

&lt;p&gt;The board from &lt;a href="https://www.coder000.com/post/waiting-room-board-signage-ux" rel="noopener noreferrer"&gt;season two's Part 6&lt;/a&gt; is genuinely good signage — if you're in the room. Real waiting rooms leak: people step out to the car park, take a toddler for a walk, go to the pharmacy next door, and come back to find their name was called three minutes ago. A companion app fixes the part the wall can't: check in from the car park, watch your place in the queue move, feel a buzz when you're next, and — the one that matters most — get a push notification when it's your turn even if the app is closed. Plus the boring essentials: where the clinic is, how far away you are, one tap for directions.&lt;/p&gt;

&lt;p&gt;Notice what's &lt;em&gt;not&lt;/em&gt; on that list. Booking stays on the website; the ticket you get there carries a six-character confirmation code, and that code is the whole credential — no account, no password, exactly as season one decided. The app is a companion to a visit that already exists, and keeping its scope that small is the first design decision of the season.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hybrid, web or native: the honest trade
&lt;/h2&gt;

&lt;p&gt;Three ways to put ClinicLive on a phone, and each has a real cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A responsive website.&lt;/strong&gt; We already have one, and it works on a phone. What it can't easily do is the pocket part: vibrate, ask for a notification permission at the right moment, reach a closed app, open the camera to scan a ticket. Some of that is possible on the web with enough work (Web Push, in particular), but it's a different project and, honestly, not the one we're doing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fully native.&lt;/strong&gt; Kotlin for Android, Swift for iOS, something else again for Windows. Three codebases for a team of one developer and one AI, and every Razor component from two seasons thrown away. Native gets the best result per platform; it also multiplies everything by three.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;.NET MAUI Blazor Hybrid&lt;/strong&gt;, which is what we picked. A MAUI app hosts a &lt;code&gt;BlazorWebView&lt;/code&gt;; your Razor components render inside it, but the C# runs natively on the device in the .NET runtime — no Blazor Server circuit, no WebAssembly download. The UI is HTML and CSS you already know, and the device is one C# call away: haptics, notifications, GPS, the camera, secure storage. The trade is that the WebView is exactly that — a web view. Anything the OS owns (the status bar, the gesture bar, the safe area, the system font) is the OS's business, not your stylesheet's. Part 2's very first screenshot is a lesson in that sentence.&lt;/p&gt;
&lt;h2&gt;
  
  
  One set of screens, three hosts
&lt;/h2&gt;

&lt;p&gt;The architecture is the argument of the season, so here it is before a single prompt. The &lt;code&gt;maui-blazor-web&lt;/code&gt; template gives you three projects; we add a fourth:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The four projects — where things live, and why&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;th&gt;What's in it&lt;/th&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ClinicLive.Contracts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The DTOs the phone and the server agree on&lt;/td&gt;
&lt;td&gt;Share the &lt;em&gt;shape&lt;/em&gt; of the data, never the server's code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ClinicLive.Pocket.Shared&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every screen, once — a Razor Class Library&lt;/td&gt;
&lt;td&gt;Nothing in here references a platform API directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ClinicLive.Pocket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The MAUI host: Android and Windows (iOS compiles; see below)&lt;/td&gt;
&lt;td&gt;Answers the shared code's questions with what the device has&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ClinicLive.Pocket.Web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A Blazor Server host for the same components&lt;/td&gt;
&lt;td&gt;Answers the same questions, honestly, as a browser&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Answers the shared code's questions" is the pattern every native feature in this season follows, and it's simple enough to show in full now. The shared project declares an interface; each host implements it with whatever it actually has. This is the first one, and its comment is the design rationale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// The first "capability interface" — the pattern every native feature in this app follows.&lt;/span&gt;
&lt;span class="c1"&gt;///&lt;/span&gt;
&lt;span class="c1"&gt;/// The shared UI asks the question ("what am I running on?"); each host answers it with&lt;/span&gt;
&lt;span class="c1"&gt;/// what it actually has. MAUI answers from DeviceInfo; the web host answers "a browser".&lt;/span&gt;
&lt;span class="c1"&gt;/// Nothing in the shared project ever references a platform API directly.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPlatformInfo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;"Android", "Windows", "iOS", "Web"…&amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;OS version string, for the About screen and bug reports.&amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;Phone-sized screen — drives the "one hand, thumbs" layout choices.&amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsPhone&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;"Native app" or "Browser" — which host is rendering these components.&amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&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;Nine of these arrive over the season — platform info, app lifecycle, haptics, notifications, push registration, location, the camera scanner, connectivity and storage — and the browser host implements every one of them, sometimes as an honest no-op ("not here"). That honesty is the point: the shared screen never has to know which host it's on, and a feature the browser can't do simply doesn't render. The web host also earns its keep as a development tool — it's the host the season-two screenshot harness can already photograph.&lt;/p&gt;

&lt;h2&gt;
  
  
  The toolchain, and one bug before a single prompt
&lt;/h2&gt;

&lt;p&gt;Nothing exotic was installed for this season. Visual Studio's MAUI workloads were already on the machine, with the Android SDK and an emulator: a Pixel 5 image running Android 13, the Play-services flavor so Firebase can deliver push later. That emulator is the phone in every Android screenshot you'll see; the Windows app runs on the same PC; the browser host runs under the same Playwright harness that photographed season two. Docker still provides PostgreSQL, exactly as in season one.&lt;/p&gt;

&lt;p&gt;The first smoke build of the untouched template found the season's first bug, and it wasn't in the template. Android built. The web host built. The Windows target failed with &lt;code&gt;MSB3030&lt;/code&gt; — "could not copy" a Windows App SDK file with a very long name — through three retries, a self-contained flag and a clean of &lt;code&gt;obj/&lt;/code&gt;. The cause was the folder: the scratch path was about 270 characters long, and the Windows App SDK's copy step dies quietly past Windows' 260-character &lt;code&gt;MAX_PATH&lt;/code&gt;. The same project in a short folder built in 9.9 seconds.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🤖 &lt;strong&gt;What the AI got wrong:&lt;/strong&gt; it spent three retries and two configuration changes on a build that was failing for a reason no configuration touches. The lesson to carry: "works on Android, fails on Windows" can be your folder name. When one target of a multi-target project fails on a file operation, look at the path before the project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What this season does not cover
&lt;/h2&gt;

&lt;p&gt;Here's the part most tutorial series skip. Four things are out of scope, and three more will turn out to be things the bench could not prove — you'll meet each one where it happens, but you deserve the list up front.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;iOS builds.&lt;/strong&gt; The MAUI project targets iOS and Mac Catalyst and they compile — but there is no Mac on this bench, so nothing was ever built or run on Apple hardware. Every iOS file you'll see in the repo is an honest stub that says so.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store publishing.&lt;/strong&gt; Part 11 produces a signed APK and app bundle for Android and a self-contained Windows folder. Nothing goes to a store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Push.&lt;/strong&gt; The browser host gets everything except push; that's a project of its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MSIX signing.&lt;/strong&gt; The Windows app ships unpackaged. Part 11 explains what a packaged identity would buy — and why that matters for the next item.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And three limits the season ran into and kept, rather than faked: Windows toast notifications never displayed in this season (the permission said "Allowed", the code returned success, no banner — Part 5 finds it, Parts 10 and 11 explain it); the Android emulator never rendered the poster a QR scanner needs, so the on-device decode in Part 8 is proven by a round-trip test rather than a photograph; and Google Maps on the emulator crashed after our directions intent had done its job (Part 7). The screenshots of all three are in the posts, exactly as they came out.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;Perishable facts, as of September 2026:&lt;/strong&gt; this season was built with Claude Code, picking a model per part — Claude Opus 5 at high effort for the judgment calls (architecture, lifecycle semantics, diagnosing the Android insets, deciding what not to fake), currently $5 in / $25 out per million tokens, and Claude Sonnet 5 at medium effort for the plumbing, currently $3 / $15. The app targets .NET 10 and MAUI 10; the emulator runs Android 13. Prices and model names churn faster than anything else in this series — re-check them before trusting the numbers. The &lt;em&gt;method&lt;/em&gt; — match the model to the stakes of the task — is the part that lasts.&lt;/p&gt;

&lt;p&gt;🧠 &lt;strong&gt;Model pick:&lt;/strong&gt; every part opens its toolbox in a box like this one. The standing rule from season one still holds: Opus, high effort, where a wrong decision is expensive to undo — Part 2's project structure and capability interfaces, Part 4's reconnect and resume semantics, Part 6's foreground-versus-closed push rules; Sonnet, medium, where the rules are already settled and the work is plumbing — the API in Part 3, geolocation in Part 7, the desk layout in Part 10, shipping in Part 11.&lt;/p&gt;

&lt;p&gt;💸 &lt;strong&gt;The meter:&lt;/strong&gt; season one closed at ≈ $6.20 and season two at ≈ $3.60; season three's meter starts fresh. Nothing is on it yet except the smoke build above, which lands in Part 2's number. A spoiler worth having up front: the whole season — three hosts, push, haptics, GPS, a camera, offline, signing and CI — comes in at about $7.20 of model usage, with two dead ends responsible for more of that than any feature.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Checkpoint:&lt;/strong&gt; there's no tag for this part — nothing was committed yet. Before Part 2 you want the MAUI workload installed, an Android emulator that boots (any recent Android will do; ours is Android 13), and season one's PostgreSQL container ready, since the clinic itself is the server the app will talk to. &lt;a href="https://github.com/rahulvyas777/clinic-live" rel="noopener noreferrer"&gt;The repo&lt;/a&gt; tags this season &lt;code&gt;pocket-02&lt;/code&gt; through &lt;code&gt;pocket-11&lt;/code&gt;, one per build part, and &lt;a href="https://www.coder000.com/series/from-prompt-to-pocket" rel="noopener noreferrer"&gt;the series page&lt;/a&gt; lists all twelve.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next: the template goes in, the template comes out, the season-two tokens move into a phone-first stylesheet, the first capability interface proves the three hosts are really rendering the same component — and the first Android screenshot catches a purple status bar and a header hidden under it. &lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-shared-ui-three-hosts" rel="noopener noreferrer"&gt;Part 2: One UI, Three Hosts: The Capability-Interface Pattern&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-companion-app-plan" rel="noopener noreferrer"&gt;coder000.com&lt;/a&gt;. Part of the series &lt;a href="https://www.coder000.com/series/from-prompt-to-pocket" rel="noopener noreferrer"&gt;From Prompt to Pocket&lt;/a&gt; — read the whole series there.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>maui</category>
      <category>blazor</category>
      <category>ai</category>
    </item>
    <item>
      <title>The Polish Retro: What a Redesign Really Costs</title>
      <dc:creator>Rahul Vyas</dc:creator>
      <pubDate>Fri, 18 Sep 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/rahulvyas777/the-polish-retro-what-a-redesign-really-costs-3edj</link>
      <guid>https://dev.to/rahulvyas777/the-polish-retro-what-a-redesign-really-costs-3edj</guid>
      <description>&lt;p&gt;Ten parts ago, ClinicLive worked but looked like the template it was born from. This series made the sequel promise: redesign it with AI and hide nothing — every direction debated, every design mistake kept, every dollar counted, and a screenshot of everything. The meter has stopped. Time to look at what we made.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gallery
&lt;/h2&gt;

&lt;p&gt;This is the payoff post, so the pictures go first. Every "before" is &lt;code&gt;part-12&lt;/code&gt; — the app exactly as season one shipped it. Every "after" is the tagged part where that surface got its treatment.&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%2Fnefmylr1jcqzivcdbmm4.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%2Fnefmylr1jcqzivcdbmm4.png" alt="The old waiting-room board: a Bootstrap page with a sidebar and small text, shown on a TV" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before&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%2Fn610kisabwrvk0lp49nv.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%2Fn610kisabwrvk0lp49nv.png" alt="The redesigned board: permanent dark teal palette, a huge serving name, capped up-next list and a live clock" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The board. Before: a web page on a TV, sidebar included — with a Register link that 404'd. After: signage — its own permanent dark palette, a 13vmin serving name readable from the back row, a real clock, and status shown only when something's wrong.&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%2F1g75v7qh1qpmamvi8k52.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%2F1g75v7qh1qpmamvi8k52.png" alt="The old booking form on a phone: a default dropdown-and-inputs Bootstrap form" width="375" height="812"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before&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%2Fbjcmynszmwke1jicfstr.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%2Fbjcmynszmwke1jicfstr.png" alt="The redesigned booking flow on a phone: slot chips grouped into Morning and Afternoon, large tap targets, a submit button reading the chosen time" width="375" height="846"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Booking, at 375px. Before: a form. After: a flow — slot chips grouped morning/afternoon, thumb-sized targets, phone keyboards wired via input types, and a submit button that says "Book 16:45" instead of "Submit."&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%2F7y9jde2rlz1xespi9f0f.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%2F7y9jde2rlz1xespi9f0f.png" alt="The old staff queue: a plain Bootstrap table of names and check-in timestamps" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before&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%2Ffmwh9is3o6ruwn16xl95.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%2Ffmwh9is3o6ruwn16xl95.png" alt="The redesigned staff queue: live stat header, elevated table card with a WAITED minutes column, and a full-width call button" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The staff queue. Before: timestamps nobody computes with. After: a live stat header and a WAITED column that turns red past 20 minutes — the number reception actually manages by, computed by the software instead of in reception's head.&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%2Fmtgtlmslpp6sxw8hz3a9.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%2Fmtgtlmslpp6sxw8hz3a9.png" alt="The old kiosk: a small text input and a standard Bootstrap button on a plain page" width="768" height="1024"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before&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%2Frq9jgzl14f30lxzq3t0d.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%2Frq9jgzl14f30lxzq3t0d.png" alt="The redesigned kiosk: giant letter-spaced code input as the centerpiece with one large check-in button" width="768" height="1024"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The kiosk. Before: a login form wearing a different label. After: a stage — 3rem letter-spaced code entry, one giant button, and (off camera) a success takeover that auto-resets after eight seconds for the next stranger in line.&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%2Fyvaznbi4sidpzuz8g7ax.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%2Fyvaznbi4sidpzuz8g7ax.png" alt="The old staff chat: messages as uniform rows in a plain list" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before&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%2F17ahq1r7y9qyvs8sr3o1.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%2F17ahq1r7y9qyvs8sr3o1.png" alt="The redesigned staff chat: message bubbles with mine right-aligned, small meta lines, and an online list with presence dots" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Staff chat. Before: a log file with a send button. After: a chat that reads like one — bubbles, mine on the right, presence dots — and, since Part 9, one that follows its own conversation.&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%2F7hn0lb3cnbuca5t391p2.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%2F7hn0lb3cnbuca5t391p2.png" alt="The booking confirmation as a ticket: dashed border, appointment day and time, and a large monospaced confirmation code" width="375" height="812"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The ticket has no "before" pair, because there was no ticket — season one confirmed bookings with a paragraph. Turning the confirmation into an object worth screenshotting was Part 4's best idea.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The bill, honestly
&lt;/h2&gt;

&lt;p&gt;Same rules as &lt;a href="https://www.coder000.com/post/ai-development-cost-retrospective" rel="noopener noreferrer"&gt;season one's retro&lt;/a&gt;: cumulative API spend from the build log as each part landed. The log kept running totals at each milestone, so Parts 1 and 3 — the direction exercise and the screenshot harness — ride inside their neighbors' readings:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What each part of the redesign cost in API spend (cumulative)&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What we designed&lt;/th&gt;
&lt;th&gt;Running total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://www.coder000.com/post/ai-ui-design-escaping-the-slop" rel="noopener noreferrer"&gt;1&lt;/a&gt;–&lt;a href="https://www.coder000.com/post/design-tokens-with-ai-blazor" rel="noopener noreferrer"&gt;2&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Four directions and a choice; the porcelain-and-petrol tokens; Bootstrap deleted&lt;/td&gt;
&lt;td&gt;≈ $1.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://www.coder000.com/post/screenshot-loop-ai-design" rel="noopener noreferrer"&gt;3&lt;/a&gt;–&lt;a href="https://www.coder000.com/post/designing-booking-flow-phone-ux" rel="noopener noreferrer"&gt;4&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;The screenshot harness; the booking flow and the ticket&lt;/td&gt;
&lt;td&gt;≈ $1.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/kiosk-touch-ux-design" rel="noopener noreferrer"&gt;5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Kiosk: giant code entry, success takeover, auto-reset&lt;/td&gt;
&lt;td&gt;≈ $2.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/waiting-room-board-signage-ux" rel="noopener noreferrer"&gt;6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Board: signage palette, vmin sizing, the clock&lt;/td&gt;
&lt;td&gt;≈ $2.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/staff-pages-pro-tool-ux" rel="noopener noreferrer"&gt;7&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Staff: stat headers, the WAITED column, chat bubbles&lt;/td&gt;
&lt;td&gt;≈ $2.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/ai-accessibility-audit-blazor" rel="noopener noreferrer"&gt;8&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Live regions, landmarks, skip link, the contrast audit&lt;/td&gt;
&lt;td&gt;≈ $3.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/micro-interactions-css-blazor" rel="noopener noreferrer"&gt;9&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Motion that explains change; the nine-line interop file&lt;/td&gt;
&lt;td&gt;≈ $3.60&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💸 &lt;strong&gt;The meter, final reading:&lt;/strong&gt; ≈ &lt;strong&gt;$3.60&lt;/strong&gt; for the entire redesign — a design system, four surfaces treated as four UX disciplines, an accessibility pass and a motion pass. Season one's &lt;a href="https://www.coder000.com/post/ai-development-cost-retrospective" rel="noopener noreferrer"&gt;$6.20&lt;/a&gt; built ClinicLive working; $3.60 more made it something people would &lt;em&gt;want&lt;/em&gt; to use. The redesign cost barely half the build — and it changed almost everything a user actually touches.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;How the meter is measured, same as season one: it's the build's actual token consumption priced at the API list rates of the models used (the 🧠 boxes name them). If you work through a subscription plan instead of pay-per-token, your bill won't literally say "$3.60" — but the tokens burned are the same, so it's the honest apples-to-apples number.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And the same caveat as last time, because it's still true: the dollars are not the real cost. The real cost is the human eye — every screenshot reviewed, every direction judged, one palette vetoed outright. The meter buys drafts; the looking is where it became a design. Keep that in mind as you read the next table, because most of it is the looking.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complete design-mistake log
&lt;/h2&gt;

&lt;p&gt;Every 🤖 from ten parts, in one table:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What the AI got wrong, part by part&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What went wrong&lt;/th&gt;
&lt;th&gt;Caught by&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 — before shots&lt;/td&gt;
&lt;td&gt;The waiting-room TV wore the app's sidebar — including a Register link that 404'd, because season one's hardening pass deleted the page and nobody deleted the link. Six parts of code review missed it.&lt;/td&gt;
&lt;td&gt;The first screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 — before shots&lt;/td&gt;
&lt;td&gt;The demo data had aged out: seeded appointments were four days stale, and the kiosk correctly refused their codes. "Correct" and "demo-able" are different properties, and screenshots test the second one.&lt;/td&gt;
&lt;td&gt;The screenshot loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 — direction&lt;/td&gt;
&lt;td&gt;The winning "Calm Practice" palette was a near-twin of the author's &lt;em&gt;other&lt;/em&gt; project — same warm cream, same greens. The AI checks its output against the world's defaults, not against your own portfolio; it has no idea what else you've shipped. Version two became porcelain and petrol.&lt;/td&gt;
&lt;td&gt;The human — nothing else could&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 — foundations&lt;/td&gt;
&lt;td&gt;Every &lt;code&gt;h1&lt;/code&gt; wore a teal focus ring — &lt;code&gt;FocusOnNavigate&lt;/code&gt; focuses the heading for screen readers, and the new global &lt;code&gt;:focus-visible&lt;/code&gt; rule happily styled it. Fix: rings on interactive elements only.&lt;/td&gt;
&lt;td&gt;The screenshot loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 — foundations&lt;/td&gt;
&lt;td&gt;"An unhandled error has occurred" visible on every page. The deleted template CSS had been quietly carrying &lt;code&gt;#blazor-error-ui { display:none }&lt;/code&gt; — the invisible parts of a template are still load-bearing.&lt;/td&gt;
&lt;td&gt;The screenshot loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 — foundations&lt;/td&gt;
&lt;td&gt;The new top bar wrapped into three awkward rows at 375px — fine at every width the AI had "seen," broken at the one patients use.&lt;/td&gt;
&lt;td&gt;The phone-width screenshot&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the right-hand column and season two's pattern appears, and it's different from season one's. There, the mistakes were about &lt;em&gt;context&lt;/em&gt; — your rules, your timezone — and the catches came from reading code against a spec. Here, almost every catch came from &lt;strong&gt;looking at rendered output&lt;/strong&gt;: the screenshot loop from &lt;a href="https://www.coder000.com/post/screenshot-loop-ai-design" rel="noopener noreferrer"&gt;Part 3&lt;/a&gt; found things code review is structurally blind to, because no diff shows you a sidebar on a TV. And the one mistake the loop could never catch — the near-twin palette — needed a reviewer with something no model has: a memory of your own portfolio.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the AI never caught alone
&lt;/h2&gt;

&lt;p&gt;The mistake table undersells the human half, because the biggest contributions weren't corrections — they were &lt;em&gt;direction&lt;/em&gt;. The AI proposed four art directions; a person chose Calm Practice and then vetoed its first palette. The WAITED column — the best feature of the staff redesign — arrived only after a human asked "what number does the person at the desk actually act on?" The kiosk's auto-reset came from "who uses this surface, and what state do they find it in?" In every case the AI executed beautifully &lt;em&gt;once the question existed&lt;/em&gt; — and would have shipped something plausible and worse if it hadn't.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🤖 &lt;strong&gt;What the AI got wrong:&lt;/strong&gt; the season in one sentence — it never had taste, it had defaults. Left unprompted it converges on the same purple-gradient slop &lt;a href="https://www.coder000.com/post/ai-ui-design-escaping-the-slop" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt; opened with; pointed at a surface with the right question, it produces work you'd pay a studio for. The AI executes judgment; it doesn't replace it. Your questions are the design.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What the AI was brilliant at
&lt;/h2&gt;

&lt;p&gt;Fairness cuts both ways, and season two's wins were real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The vocabulary migration.&lt;/strong&gt; Deleting Bootstrap while every page kept compiling — by inventorying the forty classes actually used and reimplementing them on the tokens — was the season's best engineering trick, executed flawlessly. A redesign that never breaks the build is a redesign you can ship incrementally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-surface reframing.&lt;/strong&gt; Asked "who uses this, standing where?", it re-derived each surface from first principles: vmin sizing for a wall TV, a success takeover for people standing up, density for people paid to scan. The four-disciplines structure of this series came out of those answers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The copy pass.&lt;/strong&gt; "Mornings fill up first." "That email doesn't look right — or leave it empty." "Stuck? The front desk is right there." Microcopy this humane usually requires a writer on staff; here it required asking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tireless iteration.&lt;/strong&gt; The screenshot loop ran dozens of look-adjust-reshoot rounds. A human designer's patience is a budget; the model's isn't. It never once defended a bad draft out of ego.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Model pick, the season's rule:&lt;/strong&gt; Opus wherever the question was "what &lt;em&gt;is&lt;/em&gt; this surface?" — the token system, the four directions, the board's signage reframing; Sonnet wherever execution ran against a settled system — booking, kiosk, staff pages, the motion pass. Same principle as season one, different axis: in design, the consequence of a cheap-model mistake isn't data loss, it's incoherence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The tests postscript
&lt;/h2&gt;

&lt;p&gt;One more number, and it might be the most persuasive one in the series: &lt;strong&gt;all nine tests from season one stayed green through the entire redesign&lt;/strong&gt; — every part, &lt;code&gt;polish-02&lt;/code&gt; through &lt;code&gt;polish-09&lt;/code&gt;, verified by the same CI. Ten parts of touching every page, deleting a CSS framework, rewriting every layout — and the &lt;a href="https://www.coder000.com/post/testing-ai-generated-code" rel="noopener noreferrer"&gt;harness built in season one&lt;/a&gt; silently guaranteed that none of it changed what the app &lt;em&gt;does&lt;/em&gt;. That's the compounding return on testing nobody mentions: the suite you build in season one is what makes season two safe to attempt. A redesign with no tests is a rewrite with better marketing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Checkpoint, the last one:&lt;/strong&gt; you've now shipped a &lt;em&gt;designed&lt;/em&gt; app — not just working, but chosen: a token system with a portfolio-checked palette, four surfaces treated as four disciplines, announced to screen readers, explained through motion, and honestly accounted for. Here's what's next: run this season's loop on an app of yours. Before shots first — you can't see how far you've come without them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where next 🎉
&lt;/h2&gt;

&lt;p&gt;Ten parts, one redesign, $3.60. The whole two-season arc — every prompt, every tag — lives in &lt;a href="https://github.com/rahulvyas777/clinic-live" rel="noopener noreferrer"&gt;the ClinicLive repo&lt;/a&gt;, and the README's season-two tag table maps each &lt;code&gt;polish-*&lt;/code&gt; tag to its part, so you can check out any moment of the redesign and see it with your own eyes.&lt;/p&gt;

&lt;p&gt;If you arrived here through the design door, the story starts one season earlier: &lt;a href="https://www.coder000.com/post/ai-assisted-development-with-claude" rel="noopener noreferrer"&gt;From Prompt to Production, Part 1&lt;/a&gt; builds the app this series just polished, spec to VPS, mistakes and all. The full catalog of everything on this site is on the &lt;a href="https://www.coder000.com/series" rel="noopener noreferrer"&gt;series page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And the story continues:&lt;/strong&gt; ClinicLive works and it's polished — but it lives on a kiosk and a wall. Season three, &lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-companion-app-plan" rel="noopener noreferrer"&gt;From Prompt to Pocket&lt;/a&gt;, puts it in the patient's pocket: the same Blazor screens as a .NET MAUI app on Android and Windows and as a web app, with real push notifications, haptics, geolocation, a QR scanner and offline support — every native feature a capability interface, every screenshot from a real emulator and a real desktop, every dead end kept in.&lt;/p&gt;

&lt;p&gt;One closing thought to carry off. Season one proved AI can build software with you; this season proved it can design it with you — but only steered. The slop everyone recognizes isn't what the models produce; it's what they produce &lt;em&gt;unquestioned&lt;/em&gt;. Four directions instead of one default. A screenshot instead of a hope. "Who uses this, standing where?" instead of "make it pretty." The tools bring the polish — you bring the taste, and this series' real deliverable was the habit of insisting on it.&lt;/p&gt;

&lt;p&gt;Thanks for seeing the redesign through, all ten parts of it. Now go make something of yours worth screenshotting. 🎨&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.coder000.com/post/ai-ui-design-retrospective" rel="noopener noreferrer"&gt;coder000.com&lt;/a&gt;. Part of the series &lt;a href="https://www.coder000.com/series/from-prompt-to-polish" rel="noopener noreferrer"&gt;From Prompt to Polish&lt;/a&gt; — read the whole series there.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>design</category>
      <category>ux</category>
      <category>blazor</category>
    </item>
    <item>
      <title>AI UI Design Without the Slop: Four Directions and a Choice</title>
      <dc:creator>Rahul Vyas</dc:creator>
      <pubDate>Mon, 14 Sep 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/rahulvyas777/ai-ui-design-without-the-slop-four-directions-and-a-choice-glh</link>
      <guid>https://dev.to/rahulvyas777/ai-ui-design-without-the-slop-four-directions-and-a-choice-glh</guid>
      <description>&lt;p&gt;Season one of this story, &lt;a href="https://www.coder000.com/post/ai-assisted-development-with-claude" rel="noopener noreferrer"&gt;From Prompt to Production&lt;/a&gt;, ended with &lt;a href="https://www.coder000.com/post/ai-development-cost-retrospective" rel="noopener noreferrer"&gt;a retrospective&lt;/a&gt; and a working app: ClinicLive books appointments, checks patients in at a kiosk, and calls them from a live waiting-room board — tested, hardened, deployed. It also looks exactly like the template it was born from. This series is the design sequel: same repo, same AI pair, ten parts to close the gap between "works" and "looks like someone meant it."&lt;/p&gt;

&lt;h2&gt;
  
  
  First, the before photos
&lt;/h2&gt;

&lt;p&gt;Before touching a line of CSS, we photographed everything. Partly for honesty — a redesign series that can't show its "before" is just a mood board — and partly because those photos turn out to be a working tool, not a keepsake. A quick Playwright script (it graduates into a proper harness in &lt;a href="https://www.coder000.com/post/screenshot-loop-ai-design" rel="noopener noreferrer"&gt;Part 3&lt;/a&gt;) captured all ten surfaces at their natural sizes: the booking page on a 375px phone, the kiosk on a tablet, the board and the staff pages at 1280.&lt;/p&gt;

&lt;p&gt;Here is the waiting-room board — the screen that hangs on a wall and gets read from chairs:&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%2Fnefmylr1jcqzivcdbmm4.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%2Fnefmylr1jcqzivcdbmm4.png" alt="The waiting-room board in the old template: the left third is a purple gradient app sidebar with Home, Book, Cancel a booking, Check-in kiosk, Waiting room, Register and Login links; the board content — 'Waiting room', 'The next patient will be called shortly…' and an Up next list with two names — sits in small black text on white." width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A waiting-room TV wearing an app sidebar — with Register and Login links. Fourteen parts of engineering, and the room sees a developer template.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And the booking page, the screen a stressed parent meets on a phone:&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%2F1g75v7qh1qpmamvi8k52.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%2F1g75v7qh1qpmamvi8k52.png" alt="The booking page at 375px in the old template: a purple top bar with a hamburger menu, an About link, a 'Book an appointment' heading, a date field, and twenty-two identical small time buttons from 11:30 to 16:45 in a four-column grid." width="375" height="812"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Twenty-two identical buttons and no opinion about any of them. It works — season one proved that under test. It just doesn't care.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Two bugs before a single design decision
&lt;/h2&gt;

&lt;p&gt;The before shoot was supposed to produce evidence. It produced findings. Look at that board screenshot again: the sidebar offers &lt;strong&gt;Register&lt;/strong&gt; — and Register has been a 404 since &lt;a href="https://www.coder000.com/post/ai-code-review-security-pass" rel="noopener noreferrer"&gt;Part 11's hostile review&lt;/a&gt; deleted the whole self-registration surface. Nobody removed the sidebar link. Which means the TV on the waiting-room wall spent the rest of season one offering patients a link to an error page, and it survived a test suite, a hostile security pass, and a deploy checklist — because all of those read code, and in code nothing is wrong. A link to a deleted page is perfectly valid markup. It's only wrong on a television. One photo caught it.&lt;/p&gt;

&lt;p&gt;The second finding came from the kiosk:&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%2Fwwyidd7i2y5rm630eewg.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%2Fwwyidd7i2y5rm630eewg.png" alt="The check-in kiosk in the old template: purple gradient sidebar with Home, Book, Cancel a booking, Check-in kiosk, Waiting room, Register and Login links; a 'Welcome' heading with a waving-hand emoji, a wide dotted code input, a blue Check in button, and a green alert reading 'You're checked in! You are number 2 in the queue. Watch the board for your name.'" width="768" height="1024"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The kiosk before-shot took two takes. The first run typed every seeded code and got "No appointment found" back — the demo appointments had quietly aged out, four days after &lt;a href="https://www.coder000.com/post/deploying-blazor-signalr-vps" rel="noopener noreferrer"&gt;Part 12's&lt;/a&gt; deploy. This green alert is the second take, after re-seeding.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The kiosk was being perfectly correct: those codes really did belong to appointments in the past, and refusing them is exactly what Part 11 would have demanded. But "correct" and "demo-able" are different claims, and demo data has a shelf life. Both bugs go in the log, both get fixed in the next two parts — and notice what found them: not a review prompt, not a test, a &lt;em&gt;camera&lt;/em&gt;. Hold that thought until Part 3.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why AI design converges on slop
&lt;/h2&gt;

&lt;p&gt;Now the actual subject. Ask an AI to "make it look nice, modern and professional" and you will get the same app everyone else got: a purple gradient somewhere load-bearing, Inter on white, three feature cards with icons, generous rounded corners, a hero section congratulating you on visiting. This isn't a personality flaw — it's what a language model &lt;em&gt;is&lt;/em&gt;. It predicts the likely continuation, and a generic prompt asks for the most likely design. A vague "make it nice" is, quite literally, a request for the average of the internet.&lt;/p&gt;

&lt;p&gt;The trap has a second jaw. When the average design comes back, most of us can't articulate why it's wrong — "it looks AI-generated" isn't actionable feedback — so we ask for another pass, get a slightly different average, and settle. You can't art-direct what you can't name. The fix is to change the shape of the request so that naming happens up front.&lt;/p&gt;
&lt;h2&gt;
  
  
  The technique: propose wide, choose narrow
&lt;/h2&gt;

&lt;p&gt;Instead of asking for a design, ask for &lt;em&gt;directions&lt;/em&gt; — several of them, genuinely different, each pinned down hard enough to be judged. The ask is small; the constraints are the whole trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Propose four visual directions for this app, and make them genuinely
different from each other. For each: a name, background / ink / accent
colors as hex, one typeface, and a single line on how it should feel.
No purple gradients, no default-font-on-white. Directions only — no
CSS yet."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hex codes force commitment — "warm and inviting" is not falsifiable, &lt;code&gt;#FAF7F2&lt;/code&gt; is. One line of feel keeps each direction honest about what it's for. And "no CSS yet" is the same clause that made season one's security review work: proposals first, so the human stays the judge. Here are the four that came back, exactly as proposed:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The four directions, as proposed&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Palette &amp;amp; type&lt;/th&gt;
&lt;th&gt;Feels like&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Calm Practice&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Warm paper &lt;code&gt;#FAF7F2&lt;/code&gt;, ink &lt;code&gt;#1F2A2E&lt;/code&gt;, sage &lt;code&gt;#5B8A72&lt;/code&gt;, clay &lt;code&gt;#C4664B&lt;/code&gt; for warnings · Atkinson Hyperlegible&lt;/td&gt;
&lt;td&gt;A practice you trust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Swiss Health&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pure white, near-black, a single red &lt;code&gt;#D32F2F&lt;/code&gt; accent, hard grid · Helvetica stack&lt;/td&gt;
&lt;td&gt;Precision, zero warmth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Night Shift&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dark-first slate &lt;code&gt;#16181D&lt;/code&gt;, cyan &lt;code&gt;#4DD0E1&lt;/code&gt;, glowing board&lt;/td&gt;
&lt;td&gt;Mission control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Friendly Paper&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cream, rounded everything, Fraunces serif accents&lt;/td&gt;
&lt;td&gt;A children's clinic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four real options, and now choosing is a human-sized job. B is a beautiful poster and a cold clinic. C is right for exactly one of our surfaces — remember it when the board gets its own dark treatment in &lt;a href="https://www.coder000.com/post/waiting-room-board-signage-ux" rel="noopener noreferrer"&gt;Part 6&lt;/a&gt; — and wrong for a family clinic's public pages. D risks twee the moment it meets staff-page density. A is clinic-appropriate warmth, works in light and dark, and its typeface is a story in itself. We picked A.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Model pick:&lt;/strong&gt; Opus, high effort, for the directions. Divergence is a thinking task — asked for four directions, the cheap model tends to return one direction wearing four names. Everything else in this part (the before shoot, the revision below) is pennies on any model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The human veto
&lt;/h2&gt;

&lt;p&gt;This is where the part earns its title. Tokens were about to be built on Calm Practice when a nagging feeling produced the most valuable prompt of the season so far, verbatim: &lt;em&gt;"it's not similar to my other project, right???"&lt;/em&gt; — three question marks, because the feeling was already pretty sure. We opened the stylesheets of my other project, UnifiedSelf, and checked: cream &lt;code&gt;#F4F1E8&lt;/code&gt;, green &lt;code&gt;#2C7A4B&lt;/code&gt;, sage &lt;code&gt;#A8BDB6&lt;/code&gt;, Fraunces. Direction A — warm paper, sage primary, serif-adjacent warmth — was its near-twin. The "distinctive" direction was distinctive against the internet and derivative against my own portfolio.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🤖 &lt;strong&gt;What the AI got wrong:&lt;/strong&gt; nothing it could have known — which is exactly the lesson. The model optimized "distinct from AI defaults" because that's what the prompt asked for and that's the corpus it can see. Your portfolio is the corpus it can't. "Distinctive" has two halves — distinct from the slop &lt;em&gt;and&lt;/em&gt; distinct from your own prior work — and the second half is your job to bring: paste your other projects' palettes into the prompt, or expect a twin. The veto itself also doesn't delegate: the AI rated Calm Practice distinctive right up until it was shown otherwise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  v2: porcelain &amp;amp; petrol
&lt;/h2&gt;

&lt;p&gt;The revision kept everything that was right about A — calm, light, accessible — and moved the hue away from my own shadow: cool instead of warm. &lt;strong&gt;"Calm Practice: porcelain &amp;amp; petrol"&lt;/strong&gt; — porcelain &lt;code&gt;#F5F8F8&lt;/code&gt; background, white surfaces, ink &lt;code&gt;#182A2D&lt;/code&gt;, petrol teal &lt;code&gt;#16696F&lt;/code&gt; as the primary, coral &lt;code&gt;#C96F4A&lt;/code&gt; as the accent, &lt;code&gt;#3E8460&lt;/code&gt; for good news and &lt;code&gt;#B4473C&lt;/code&gt; for bad. The dark set was priced on day one rather than bolted on later: &lt;code&gt;#101B1D&lt;/code&gt; background, &lt;code&gt;#182527&lt;/code&gt; surfaces, ink &lt;code&gt;#E4EDEC&lt;/code&gt;, primary lifted to &lt;code&gt;#4FB3B8&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The typeface survived both versions, and deserves its sentence: &lt;strong&gt;Atkinson Hyperlegible&lt;/strong&gt; was designed by the Braille Institute for low-vision readers — its letterforms are deliberately unambiguous, so 1, l and I, or 0 and O, can't impersonate each other. A clinic's UI is read by anxious people, older people, and people squinting at a wall from five meters. That's an accessibility story built into a font choice, and &lt;a href="https://www.coder000.com/post/ai-accessibility-audit-blazor" rel="noopener noreferrer"&gt;Part 8&lt;/a&gt; will hold the rest of the design to the same standard.&lt;/p&gt;

&lt;h2&gt;
  
  
  The plan for the season
&lt;/h2&gt;

&lt;p&gt;Two strategy decisions are already made, and both come from treating the redesign like the engineering project it is. First, foundations before beauty: Part 2 builds a token system and — the migration trick of the season — deletes Bootstrap while keeping its class &lt;em&gt;vocabulary&lt;/em&gt;, so every page keeps compiling. The kiosk and the board become chrome-free "appliances," which is also the structural fix for that Register link. Second, after Part 3 makes the screenshot loop a real tool, each surface gets its own part as its own UX discipline: phone, touch, signage, pro tool. Four surfaces, four different sets of rules — that's the argument of the whole series.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💸 &lt;strong&gt;The meter:&lt;/strong&gt; season one closed at ≈ $6.20 total; season two's meter starts fresh. ≈ $0.40 so far — the before shoot, four directions, and the porcelain &amp;amp; petrol revision. The most valuable contribution of the part — "it's not similar to my other project, right???" — was typed by a human, free of charge.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Checkpoint:&lt;/strong&gt; &lt;code&gt;git checkout part-12&lt;/code&gt; in &lt;a href="https://github.com/rahulvyas777/clinic-live" rel="noopener noreferrer"&gt;the repo&lt;/a&gt; is forever the before photo: the board wears its sidebar, the sidebar offers Register, and Register 404s. The before screenshots are committed under &lt;code&gt;shots/before&lt;/code&gt; — the next nine parts argue against that folder.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, the foundation every later part will spend: every color a custom property, dark mode by redefining tokens instead of touching components, and Bootstrap deleted on the same day every page keeps compiling — &lt;a href="https://www.coder000.com/post/design-tokens-with-ai-blazor" rel="noopener noreferrer"&gt;Part 2: Foundations: design tokens, dark mode — and killing Bootstrap&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.coder000.com/post/ai-ui-design-escaping-the-slop" rel="noopener noreferrer"&gt;coder000.com&lt;/a&gt;. Part of the series &lt;a href="https://www.coder000.com/series/from-prompt-to-polish" rel="noopener noreferrer"&gt;From Prompt to Polish&lt;/a&gt; — read the whole series there.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>design</category>
      <category>ui</category>
      <category>blazor</category>
    </item>
    <item>
      <title>The Retro: What ClinicLive Really Cost, and What the AI Got Wrong</title>
      <dc:creator>Rahul Vyas</dc:creator>
      <pubDate>Fri, 11 Sep 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/rahulvyas777/the-retro-what-cliniclive-really-cost-and-what-the-ai-got-wrong-2dg</link>
      <guid>https://dev.to/rahulvyas777/the-retro-what-cliniclive-really-cost-and-what-the-ai-got-wrong-2dg</guid>
      <description>&lt;p&gt;Fourteen parts ago, this series made a promise: build a real app with AI and hide nothing — every prompt shown, every mistake kept, every dollar counted. ClinicLive is live behind nginx, its commit messages are the prompt log, and the meter has stopped. Time to count.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bill, honestly
&lt;/h2&gt;

&lt;p&gt;These numbers come straight from the build log — cumulative API spend as each part of &lt;a href="https://github.com/rahulvyas777/clinic-live" rel="noopener noreferrer"&gt;the app&lt;/a&gt; landed:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What each part of ClinicLive cost in API spend&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What we built&lt;/th&gt;
&lt;th&gt;Running total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/writing-a-software-spec-with-ai" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The spec (plus 9 clarifying questions)&lt;/td&gt;
&lt;td&gt;≈ $0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/designing-postgresql-schema-with-ai" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL schema&lt;/td&gt;
&lt;td&gt;≈ $0.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/scaffolding-blazor-server-with-ai" rel="noopener noreferrer"&gt;5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Blazor Server skeleton, entities, migration, seeder&lt;/td&gt;
&lt;td&gt;≈ $1.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/building-crud-features-with-ai" rel="noopener noreferrer"&gt;6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The booking flow&lt;/td&gt;
&lt;td&gt;≈ $1.80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/signalr-live-updates-with-ai" rel="noopener noreferrer"&gt;7&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Live queue: kiosk, board, SignalR hub&lt;/td&gt;
&lt;td&gt;≈ $2.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/blazor-server-chat-presence-signalr" rel="noopener noreferrer"&gt;8&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Staff chat, presence, typing&lt;/td&gt;
&lt;td&gt;≈ $3.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/testing-ai-generated-code" rel="noopener noreferrer"&gt;9&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Test suite vs a real PostgreSQL&lt;/td&gt;
&lt;td&gt;≈ $4.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/debugging-with-ai" rel="noopener noreferrer"&gt;10&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Two real bug hunts&lt;/td&gt;
&lt;td&gt;≈ $4.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/ai-code-review-security-pass" rel="noopener noreferrer"&gt;11&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The hardening pass&lt;/td&gt;
&lt;td&gt;≈ $5.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/deploying-blazor-signalr-vps" rel="noopener noreferrer"&gt;12&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Deploy runbook, nginx, systemd, CI&lt;/td&gt;
&lt;td&gt;≈ $6.20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💸 &lt;strong&gt;The meter, final reading:&lt;/strong&gt; ≈ $6.20 of API spend and roughly one hour of wall-clock AI-assisted building — for a Blazor Server app with PostgreSQL, two SignalR surfaces, auth, a nine-test suite against a real database, CI, and deploy artifacts. An honest estimate for hand-building the same thing: two to three &lt;em&gt;days&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;How the meter is measured, throughout this series: it's the build's actual token consumption priced at the API list rates of the models used (the 🧠 boxes name them). If you work through a subscription plan instead of pay-per-token, your bill won't say "$6.20" — but the tokens burned are the same, so it's the honest apples-to-apples number.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now the honesty that belongs next to that callout: &lt;strong&gt;the $6.20 is not the real cost.&lt;/strong&gt; The real cost is the human time steering and reviewing — reading every diff, pasting the schema into a review before accepting it, saying no to SMS, noticing that "Test G" isn't "Test G." That time doesn't appear on any meter, and it is precisely the part you must not skip — because every mistake in the next table was caught by exactly that time, and the one that wasn't caught early (open self-registration) sat in the codebase for six parts. The dollars buy drafts. The review is where the app became correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complete mistake log
&lt;/h2&gt;

&lt;p&gt;Every 🤖 from fourteen parts, in one table:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What the AI got wrong, part by part&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What the AI got wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3 — spec&lt;/td&gt;
&lt;td&gt;Invented an SMS notification feature nobody asked for; wanted patient accounts until the non-goal was stated explicitly. Scope creep is a habit, not an accident.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 — schema&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;timestamp&lt;/code&gt; without a timezone; no index on the &lt;code&gt;appointment.patient_id&lt;/code&gt; foreign key; a plain UNIQUE on the slot that would have blocked re-booking a cancelled appointment. The partial-index requirement had to come from us.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 — skeleton&lt;/td&gt;
&lt;td&gt;Left the SQLite package in the csproj "for tests"; didn't flag that the template's confirmed-account default would lock out every seeded staff login until we asked "why can't I log in?"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6 — booking&lt;/td&gt;
&lt;td&gt;Treated clinic hours as UTC — wrong "today" for any clinic east of UTC, slot labels shifted by the offset. Compiled, demoed fine, quietly wrong about time. Fixed in Part 10.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 — queue&lt;/td&gt;
&lt;td&gt;CS0542: injected a &lt;code&gt;QueueService Queue&lt;/code&gt; member into &lt;code&gt;Queue.razor&lt;/code&gt; — a component can't have a member named after its own class.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 — chat&lt;/td&gt;
&lt;td&gt;Proposed an &lt;code&gt;[Authorize]&lt;/code&gt; hub that a server-side component can never reach (the connection carries no browser cookie — 401 forever). And CS0542 &lt;em&gt;again&lt;/em&gt;, Chat in &lt;code&gt;Chat.razor&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9 — tests&lt;/td&gt;
&lt;td&gt;Wrote the queue-order test by reading the code instead of the spec — a tautological test that passed against a bug. Also asserted "Test G" where the board masks to "Test G."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11 — hardening&lt;/td&gt;
&lt;td&gt;The template's open self-registration had shipped unquestioned in Part 5: anyone on the network could create an account and walk into the staff area. Found only when we made the AI attack its own app.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sort that table by &lt;em&gt;kind&lt;/em&gt; and a pattern jumps out. Syntax barely features — two CS0542s across fourteen parts, both fixed in seconds, is the entire compiler-error tally. Where the AI actually failed was &lt;strong&gt;your context&lt;/strong&gt;: business rules (the queue order lived in the spec, not the code), time (whose "today"?), auth reality (which connection carries which cookie), scope (what "version 1" means). The training data taught it the world's code; nothing taught it your clinic. And notice the repeats — the same CS0542 twice, the same confident tone before and after every mistake. It doesn't learn your lessons between sessions. You have to carry them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🤖 &lt;strong&gt;What the AI got wrong:&lt;/strong&gt; this whole post, condensed — almost never the syntax, reliably the context. Every mistake worth logging was about our rules, our timezone, our auth, our scope. That's not a flaw to wait out with better models; it's the permanent shape of the division of labor. The context is your job.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What the AI was brilliant at
&lt;/h2&gt;

&lt;p&gt;Fairness cuts both ways, and the wins were not small:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The scaffold.&lt;/strong&gt; A complete Blazor Server solution with Identity, wired and running, in minutes. Nobody types that faster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The translation work.&lt;/strong&gt; Spec to DDL, schema doc to EF Core entities, requirements to EditForms — mapping one precise representation onto another is its native gear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The test harness.&lt;/strong&gt; The whole Testcontainers rig, and when told "no mocking library", a hand-written 30-line no-op &lt;code&gt;IHubContext&lt;/code&gt; — a genuinely tasteful choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The runbook.&lt;/strong&gt; Part 12's DEPLOY.md, 2am checklist included, is better deployment documentation than most funded products ship.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explaining every error instantly.&lt;/strong&gt; CS0542, "pending model changes", the 401 loop — each diagnosed and explained in seconds. As a tutor sitting inside your terminal, it has no precedent.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;Model pick, the series rule:&lt;/strong&gt; Opus-class where mistakes compound — the spec, the schema, the real-time design; Sonnet-class where the path is well-worn — the scaffold, CRUD, tests; cheap models for the mechanical. Fourteen parts never needed the rule revised: match spend to the consequence of error.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Saying "I built it with AI" in an interview
&lt;/h2&gt;

&lt;p&gt;Done wrong, it sounds like a confession. Done right, it's a hiring signal — and the difference is entirely in the story you can tell. Here's the ClinicLive version: the spec came first, and the AI interviewed &lt;em&gt;me&lt;/em&gt; before writing it. Every prompt is recorded in a commit message, so the process is reviewable end to end. The tests were written from the spec, not from the code — and I can explain the tautological-test trap that rule prevents. A hostile review pass caught open self-registration before it shipped. I read every diff.&lt;/p&gt;

&lt;p&gt;That story demonstrates spec thinking, review discipline, testing judgment and security awareness — the things interviews are trying to detect. The fear behind the question was always "can this person function without the tool?", and a story about &lt;em&gt;directing&lt;/em&gt; the tool answers it better than pretending you don't use one. If interviews are on your horizon, the &lt;a href="https://www.coder000.com/interview-prep" rel="noopener noreferrer"&gt;interview prep hub&lt;/a&gt; is built for this, and &lt;a href="https://www.coder000.com/post/ai-engineer-interview-prompt-context-questions" rel="noopener noreferrer"&gt;the AI interview series' prompting part&lt;/a&gt; turns this series' habits into answers you can give under pressure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping sharp
&lt;/h2&gt;

&lt;p&gt;The whole series' discipline, compressed to four lines worth pinning above your desk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read every diff.&lt;/strong&gt; No exceptions, no "it's probably fine".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hand-write something weekly.&lt;/strong&gt; The day you can't write it is the day you can't review it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Own the spec.&lt;/strong&gt; Deciding what "correct" means is never delegated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never ship what you can't explain.&lt;/strong&gt; If you can't explain it, it isn't yours yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Checkpoint, the last one:&lt;/strong&gt; you've shipped a real app with AI — spec to schema to SignalR to a VPS, tested against a real database, hardened, deployed, and honestly accounted for. That's not a tutorial completed; that's a capability. Here's what to build next: something of yours, same discipline, prompts in the commits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where next 🎉
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;And the story continues:&lt;/strong&gt; ClinicLive works — but it still looks like the template it was born from. Season two, &lt;a href="https://www.coder000.com/post/ai-ui-design-escaping-the-slop" rel="noopener noreferrer"&gt;From Prompt to Polish&lt;/a&gt;, redesigns it with AI: a real design system, four surfaces treated as four UX disciplines, and before/after screenshots of every step. Season three, &lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-companion-app-plan" rel="noopener noreferrer"&gt;From Prompt to Pocket&lt;/a&gt;, then takes the same screens into a .NET MAUI app on Android and Windows — push notifications, haptics, a QR scanner and all.&lt;/p&gt;

&lt;p&gt;Fourteen parts, one app, $6.20. If this series left you wanting to go deeper rather than wider: the &lt;a href="https://www.coder000.com/post/blazor-tutorial-getting-started" rel="noopener noreferrer"&gt;Blazor series&lt;/a&gt; builds the UI-side fundamentals this series moved fast over, and the &lt;a href="https://www.coder000.com/post/sql-server-to-postgresql-why-switch" rel="noopener noreferrer"&gt;PostgreSQL series&lt;/a&gt; gives the database the attention it deserved here. And if the commit-messages-as-prompt-log trick appealed to you, that only works when git is second nature — the &lt;a href="https://www.coder000.com/post/git-history-why-version-control-exists" rel="noopener noreferrer"&gt;Git &amp;amp; GitHub workshop&lt;/a&gt; starts from the very beginning.&lt;/p&gt;

&lt;p&gt;Got a war story — an app you built this way, a mistake &lt;em&gt;your&lt;/em&gt; AI made that belongs in somebody's table? Tell me through the &lt;a href="https://www.coder000.com/contact" rel="noopener noreferrer"&gt;contact page&lt;/a&gt;. I read every message, and the best stories become posts.&lt;/p&gt;

&lt;p&gt;The models in this series will be museum pieces in three years. The discipline won't: own the spec, read the diff, define correct, explain what you ship. The tools multiply judgment — so grow the judgment, and let them multiply it.&lt;/p&gt;

&lt;p&gt;Thanks for building all fourteen parts with me. Now go build something of your own. 🤖&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.coder000.com/post/ai-development-cost-retrospective" rel="noopener noreferrer"&gt;coder000.com&lt;/a&gt;. Part of the series &lt;a href="https://www.coder000.com/series/from-prompt-to-production" rel="noopener noreferrer"&gt;From Prompt to Production&lt;/a&gt; — read the whole series there.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>dotnet</category>
      <category>blazor</category>
      <category>career</category>
    </item>
    <item>
      <title>AI-Assisted Development in 2026: Tools, Models and Honest Expectations</title>
      <dc:creator>Rahul Vyas</dc:creator>
      <pubDate>Mon, 07 Sep 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/rahulvyas777/ai-assisted-development-in-2026-tools-models-and-honest-expectations-4ld4</link>
      <guid>https://dev.to/rahulvyas777/ai-assisted-development-in-2026-tools-models-and-honest-expectations-4ld4</guid>
      <description>&lt;p&gt;Over the next fourteen parts we're going to build one real application — a clinic booking-and-queue system with Blazor Server, PostgreSQL and SignalR — and an AI is going to write most of the code. We'll show every prompt, keep every mistake in, and count every dollar. This first part is the map: the tools, the models, and what you can honestly expect before we type a single prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two ways to use AI for code
&lt;/h2&gt;

&lt;p&gt;If you've never used AI tooling, here's the landscape in one paragraph. The first way is &lt;strong&gt;chat&lt;/strong&gt;: you open a chat window, paste some code, ask a question, and copy the answer back into your editor. It works — plenty of developers learned a lot this way — but notice who's doing the manual labour. The model only knows what you paste, it can't run anything, and &lt;em&gt;you&lt;/em&gt; are the clipboard, ferrying code back and forth and hoping you pasted enough context.&lt;/p&gt;

&lt;p&gt;The second way is &lt;strong&gt;agentic&lt;/strong&gt;: a tool that lives in your project folder, reads your files itself, proposes edits as reviewable diffs, runs your build and your tests, and asks permission before doing anything. You stop being the clipboard and become the reviewer — which, as we'll see, is the job that actually matters.&lt;/p&gt;

&lt;p&gt;This series uses &lt;strong&gt;Claude Code&lt;/strong&gt;, Anthropic's agentic tool. It comes in whichever shape suits you: a command-line tool, a desktop app for Mac and Windows, a web version at claude.ai/code, and IDE extensions for VS Code and JetBrains. Everything in this series works the same in all of them; we'll set it up properly in &lt;a href="https://www.coder000.com/post/claude-code-setup-for-dotnet" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The method: prompt, review, refine, commit
&lt;/h2&gt;

&lt;p&gt;Before tools and prices, the thing that actually decides whether AI development goes well. Everything we build in this series follows one loop:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(diagram on the original post — the AI development loop: you write a prompt, the AI writes code, you review and test it; problems loop back into a refined prompt, good code gets committed.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You describe what you want and why. The AI writes code you didn't type. Then comes the step that separates professionals from headlines: &lt;strong&gt;you read it, run it, and test it&lt;/strong&gt;. Code that isn't right doesn't get argued with — it feeds a sharper, smaller prompt. Code that is right gets committed in a small, reviewable piece. That's the whole method. Every part of this series is this loop, applied to a different problem, and the review step is the one we never skip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which model, when — and what it costs
&lt;/h2&gt;

&lt;p&gt;"AI" isn't one thing you talk to; you choose a model per task, and the choice is mostly about money and thinking depth. Models are priced per &lt;em&gt;token&lt;/em&gt; — roughly three-quarters of a word — and you pay separately for what the model reads (input) and what it writes (output). Here's the current Claude line-up:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Claude models, August 2026 — prices per million tokens (input / output)&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Price in / out&lt;/th&gt;
&lt;th&gt;What it's for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude Opus 5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$5 / $25&lt;/td&gt;
&lt;td&gt;The deep-work default: hard design, debugging, code review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude Sonnet 5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$3 / $15&lt;/td&gt;
&lt;td&gt;The everyday coding workhorse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude Haiku 4.5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$1 / $5&lt;/td&gt;
&lt;td&gt;Quick questions and mechanical edits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each model also takes an &lt;strong&gt;effort&lt;/strong&gt; setting, from low to max, which controls how hard it thinks before answering. High effort on a hard problem buys real quality; high effort on "rename this variable" buys you a more expensive rename. Our rule of thumb, which the whole series follows:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The rule of thumb this series lives by&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The task&lt;/th&gt;
&lt;th&gt;The pick&lt;/th&gt;
&lt;th&gt;Because&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Writing a spec, designing a schema, reviewing code&lt;/td&gt;
&lt;td&gt;Opus, high effort&lt;/td&gt;
&lt;td&gt;Mistakes here compound for months; thinking is cheap by comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRUD features, scaffolding, tests&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;Well-trodden ground; the workhorse handles it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Rename this everywhere", formatting, tiny edits&lt;/td&gt;
&lt;td&gt;Haiku&lt;/td&gt;
&lt;td&gt;Mechanical work deserves mechanical prices&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;Note:&lt;/strong&gt; everything above is true &lt;em&gt;as of August 2026&lt;/em&gt;. Models and prices churn faster than any other fact in this series — this is the one section you should re-check against current pricing before trusting the numbers. The &lt;em&gt;method&lt;/em&gt; — match the model to the stakes of the task — outlives any price list.&lt;/p&gt;

&lt;p&gt;🧠 &lt;strong&gt;Model pick:&lt;/strong&gt; every part of this series opens its toolbox in a box like this one — which model, what effort, and why. This part is all reading, so nothing was billed to the build yet; the standing picks are the table above, and you'll see us follow it: Opus for the spec and schema in Parts 3–4, Sonnet for the scaffolding in Part 5.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Honest expectations
&lt;/h2&gt;

&lt;p&gt;Here's the part most AI content skips. These tools are genuinely, sometimes shockingly good at a specific set of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate&lt;/strong&gt; — the code you've written a hundred times and never enjoyed once: DTOs, mappings, configuration, test scaffolding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translation between technologies&lt;/strong&gt; — "this is how I did it in SQL Server, show me the PostgreSQL way" is a question AI answers better than most search results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First drafts&lt;/strong&gt; — a working starting point in minutes, for you to shape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explaining errors&lt;/strong&gt; — paste a stack trace and get a plain-English account of what went wrong, and usually why.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And there's a set of things it reliably gets wrong, no matter how impressive it looks while doing them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your business rules&lt;/strong&gt; — the AI has never met your clinic, your users, or your edge cases. It will guess, confidently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time zones&lt;/strong&gt; — AI-written date handling that compiles and demos fine can still be quietly wrong about what "today" means. Ask us how we know (Part 10).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security defaults&lt;/strong&gt; — it happily ships whatever a template ships, including the parts a hostile reviewer would circle in red (Part 11).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Its own blind spots&lt;/strong&gt; — it can't tell you what it doesn't know, and it repeats its habits. Wait until you see it make the exact same compiler error in two different parts.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🤖 &lt;strong&gt;What the AI got wrong:&lt;/strong&gt; nothing yet — we haven't asked it for anything. But this box appears in every part, because keeping the mistakes in is the point of this series. Coming attractions, all real: an SMS feature nobody asked for (Part 3), timestamps without time zones (Part 4), a chat design that fails authentication forever (Part 8), and a test that passes &lt;em&gt;because&lt;/em&gt; the code is wrong (Part 9). The human stays the pilot. That's not a disclaimer; it's the method.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What we're building: ClinicLive
&lt;/h2&gt;

&lt;p&gt;A real app needs a real problem, so meet &lt;strong&gt;ClinicLive&lt;/strong&gt;: front-desk software for a small clinic. Patients book a 15-minute appointment from their phone — no account, no password, just a short confirmation code. A tablet at the door lets them check in with that code. A waiting-room screen updates by itself the second someone checks in. And behind the login, reception and practitioners see today's schedule, call the next patient, and chat with each other — with presence and typing indicators — instead of shouting down the corridor.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(diagram on the original post — ClinicLive architecture: the booking page, check-in kiosk and waiting-room board talk to the Blazor Server app; the QueueHub pushes live updates out; EF Core persists everything to PostgreSQL.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That little diagram is the whole build: three browser surfaces, one Blazor Server app, one PostgreSQL database, and SignalR pushing changes out so nobody ever presses refresh. It's small enough to finish and real enough to hurt — bookings can race, queues can be ordered wrong, and time zones can lie. Perfect.&lt;/p&gt;

&lt;p&gt;The finished code lives in the companion repository at &lt;a href="https://github.com/rahulvyas777/clinic-live" rel="noopener noreferrer"&gt;github.com/rahulvyas777/clinic-live&lt;/a&gt;, and it has one unusual property: &lt;strong&gt;every commit message contains the actual prompt that produced the commit&lt;/strong&gt;. The repo's history &lt;em&gt;is&lt;/em&gt; the series. From Part 3 onward there's a tag per part, so you can check out the code exactly as it stood at the end of any chapter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Copilot, Cursor and the rest?
&lt;/h2&gt;

&lt;p&gt;Alternatives absolutely exist — GitHub Copilot, Cursor, Google's Gemini CLI, and more arriving monthly — and they're good tools; the loop in the figure above applies to all of them. We use Claude Code for this series for an unexciting reason: going deep on one tool teaches you more than a survey of five, and depth is what you can carry to whichever tool your team ends up using. If your instinct is to comparison-shop first, our &lt;a href="https://www.coder000.com/post/what-is-ai-assisted-coding" rel="noopener noreferrer"&gt;beginner's guide to AI-assisted coding&lt;/a&gt; surveys the field; this series is the deep end.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💸 &lt;strong&gt;The meter:&lt;/strong&gt; our build so far: &lt;strong&gt;$0.00&lt;/strong&gt;. Every part reports the running total, honestly. And a spoiler worth having up front: the entire fourteen-part build — spec, schema, features, real-time, tests, debugging, hardening and deployment — lands at about six dollars of model usage. Hold that number next to any hype you've heard, in either direction.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Checkpoint:&lt;/strong&gt; before Part 2 you should be able to say what separates chat from agentic tools, name which Claude model you'd pick for a schema design versus a mechanical rename, and sketch ClinicLive's three surfaces from memory. No installs yet — but having an Anthropic account ready (a paid Claude plan or API billing, either works) will make the next part a straight run.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The map is drawn. In &lt;a href="https://www.coder000.com/post/claude-code-setup-for-dotnet" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt;, Claude Code goes onto your machine, learns your project's house rules from a file called CLAUDE.md, and makes its first change to real code — reviewed, understood, and committed like you mean it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.coder000.com/post/ai-assisted-development-with-claude" rel="noopener noreferrer"&gt;coder000.com&lt;/a&gt;. Part of the series &lt;a href="https://www.coder000.com/series/from-prompt-to-production" rel="noopener noreferrer"&gt;From Prompt to Production&lt;/a&gt; — read the whole series there.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>dotnet</category>
      <category>blazor</category>
      <category>claude</category>
    </item>
    <item>
      <title>What Building a Real App with AI Actually Cost: 36 Parts, Three Seasons, About $17</title>
      <dc:creator>Rahul Vyas</dc:creator>
      <pubDate>Fri, 04 Sep 2026 01:07:45 +0000</pubDate>
      <link>https://dev.to/rahulvyas777/what-building-a-real-app-with-ai-actually-cost-36-parts-three-seasons-about-17-2ofi</link>
      <guid>https://dev.to/rahulvyas777/what-building-a-real-app-with-ai-actually-cost-36-parts-three-seasons-about-17-2ofi</guid>
      <description>&lt;p&gt;Three seasons, thirty-six parts, one clinic app — and a meter that ran the whole time. Every part of the ClinicLive trilogy ended with a 💸 box, every retro added its season up, and every number came from the build log rather than from memory. This post puts all of it in one place: what the whole thing cost, part by part and model by model, what caught the AI's mistakes, and what the money could not buy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About $17.&lt;/strong&gt; Building ClinicLive with Claude Code cost ≈ $17.00 of API spend across 36 parts and three seasons: $6.20 for a shipped Blazor Server app, $3.60 for its redesign, and $7.20 for a .NET MAUI companion app on three hosts — with 28 tests green at the end and three public tag ranges in &lt;a href="https://github.com/rahulvyas777/clinic-live" rel="noopener noreferrer"&gt;the repo&lt;/a&gt; so you can check out any moment of it. One caveat before anything else, in the words of the footnote every retro carries: on a subscription plan your bill won't literally say $17 — but the tokens burned are the same, so it's the honest apples-to-apples number.&lt;/p&gt;

&lt;h2&gt;
  
  
  The meter, all three seasons
&lt;/h2&gt;

&lt;p&gt;Here is every reading on one chart. The top row is what each part added; the bottom row is the trilogy's running total, which starts at forty cents for a spec and stops at seventeen dollars for a signed APK.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(chart on the original post — API spend per part across the three ClinicLive seasons, with the trilogy's running total underneath. Season one, 14 parts, $6.20; season two, 10 parts, $3.60; season three, 12 parts, $7.20. The running total climbs from $0.40 at season one's spec to $17.00 at season three's ship-it part. The most expensive single reading is season three's Parts 1 and 2 together, at $1.30.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Three things jump out before any table. First, no part cost more than $1.30, and most cost less than a dollar — the tallest bars are the parts that opened a season, where the architecture decisions and a lot of screenshots happen at once. Second, the seasons are more alike than different: the redesign was the cheapest, but not by the margin "it's only CSS" would predict, because a model looking at screenshots is burning image tokens. Third, the dead ends are in there. Season one's failed first chat design, season three's Windows toast chase and its forty-minute hunt for a QR poster the emulator never drew — all billed at the same rate as the features, and all left in the number on purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Season by season
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;What each season cost, and what it shipped&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Season&lt;/th&gt;
&lt;th&gt;Parts&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;th&gt;Per part&lt;/th&gt;
&lt;th&gt;What shipped&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/ai-assisted-development-with-claude" rel="noopener noreferrer"&gt;One · From Prompt to Production&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;≈ $6.20&lt;/td&gt;
&lt;td&gt;≈ $0.44&lt;/td&gt;
&lt;td&gt;A Blazor Server app: PostgreSQL schema, booking, a live queue and board over SignalR, staff chat, nine tests against a real database, a hardening pass, an nginx and systemd deploy, CI (&lt;code&gt;part-03&lt;/code&gt; to &lt;code&gt;part-12&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/ai-ui-design-escaping-the-slop" rel="noopener noreferrer"&gt;Two · From Prompt to Polish&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;≈ $3.60&lt;/td&gt;
&lt;td&gt;≈ $0.36&lt;/td&gt;
&lt;td&gt;A token-based design system with dark mode, Bootstrap deleted, four surfaces redesigned as four UX disciplines, an accessibility pass and a motion pass — nine tests still green (&lt;code&gt;polish-02&lt;/code&gt; to &lt;code&gt;polish-09&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-companion-app-plan" rel="noopener noreferrer"&gt;Three · From Prompt to Pocket&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;≈ $7.20&lt;/td&gt;
&lt;td&gt;≈ $0.60&lt;/td&gt;
&lt;td&gt;A .NET MAUI Blazor Hybrid companion app on Android, Windows and the web: nine capability interfaces, a live queue, push, haptics, geolocation, a QR scanner, offline; a signed APK, a self-contained Windows build, CI — 28 tests (&lt;code&gt;pocket-02&lt;/code&gt; to &lt;code&gt;pocket-11&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The trilogy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;36&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;≈ $17.00&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;≈ $0.47&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One app, three seasons, everything above&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Per part" divides by every part of a season, including the ones that built nothing — the intros, season one's power-moves tour, and the three retros. Divide by the parts that actually moved the meter and the numbers are $0.62, $0.40 and $0.72. Either way, the honest unit is "less than a coffee per part."&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the numbers come from
&lt;/h2&gt;

&lt;p&gt;Three retros, reconciled. &lt;a href="https://www.coder000.com/post/ai-development-cost-retrospective" rel="noopener noreferrer"&gt;Season one's retro&lt;/a&gt; lists a running total for each of Parts 3 to 12, and the build log agrees with it to the cent. &lt;a href="https://www.coder000.com/post/ai-ui-design-retrospective" rel="noopener noreferrer"&gt;Season two's retro&lt;/a&gt; merged Parts 1–2 and 3–4 into two rows because the log only recorded a reading at Parts 2 and 4; the per-part numbers for Parts 1 and 3 below ($0.40, then $1.30 on the running meter) come from those parts' own 💸 boxes, which the retro didn't repeat. &lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-retrospective" rel="noopener noreferrer"&gt;Season three's retro&lt;/a&gt; has Part 1 riding inside Part 2's reading, because the plan had no commit and its smoke build landed in Part 2. Nothing here is a new measurement — it's the same 29 readings, laid end to end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which model did what, and what the split cost
&lt;/h2&gt;

&lt;p&gt;Every part opened with a 🧠 box naming its model, and the rule never changed across three seasons: Opus where a mistake would compound — a spec, a schema, real-time semantics, a diagnosis, a review — and Sonnet where the path was well-worn: scaffolding, CRUD, tests, CSS, a documented toolchain. Here is how that rule played out, part by part, with what each part added to the meter.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Model per part across all 36 parts, and what each part added&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Season one · From Prompt to Production&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What it built&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;th&gt;Added&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/ai-assisted-development-with-claude" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The landscape — reading, no build&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Nothing billed&lt;/td&gt;
&lt;td&gt;$0.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/claude-code-setup-for-dotnet" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Claude Code setup, a two-line change&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;Everyday work&lt;/td&gt;
&lt;td&gt;≈ $0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/writing-a-software-spec-with-ai" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The spec, nine clarifying questions&lt;/td&gt;
&lt;td&gt;Opus&lt;/td&gt;
&lt;td&gt;Decisions that compound&lt;/td&gt;
&lt;td&gt;$0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/designing-postgresql-schema-with-ai" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL schema&lt;/td&gt;
&lt;td&gt;Opus&lt;/td&gt;
&lt;td&gt;Decisions that compound&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/scaffolding-blazor-server-with-ai" rel="noopener noreferrer"&gt;5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Skeleton, entities, migration, seeder&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;Mechanical, well-trodden&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/building-crud-features-with-ai" rel="noopener noreferrer"&gt;6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The booking flow&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;CRUD on a written spec&lt;/td&gt;
&lt;td&gt;$0.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/signalr-live-updates-with-ai" rel="noopener noreferrer"&gt;7&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Live queue: kiosk, board, SignalR hub&lt;/td&gt;
&lt;td&gt;Opus&lt;/td&gt;
&lt;td&gt;Real-time semantics&lt;/td&gt;
&lt;td&gt;$0.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/blazor-server-chat-presence-signalr" rel="noopener noreferrer"&gt;8&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Staff chat, presence, typing&lt;/td&gt;
&lt;td&gt;Opus&lt;/td&gt;
&lt;td&gt;One architectural judgment&lt;/td&gt;
&lt;td&gt;$0.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/testing-ai-generated-code" rel="noopener noreferrer"&gt;9&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Tests against a real PostgreSQL&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;Mechanical scaffolding&lt;/td&gt;
&lt;td&gt;$0.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/debugging-with-ai" rel="noopener noreferrer"&gt;10&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Two bug hunts&lt;/td&gt;
&lt;td&gt;Opus&lt;/td&gt;
&lt;td&gt;Diagnosis&lt;/td&gt;
&lt;td&gt;$0.80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/ai-code-review-security-pass" rel="noopener noreferrer"&gt;11&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The hardening pass&lt;/td&gt;
&lt;td&gt;Opus&lt;/td&gt;
&lt;td&gt;Adversarial review&lt;/td&gt;
&lt;td&gt;$0.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/deploying-blazor-signalr-vps" rel="noopener noreferrer"&gt;12&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Runbook, nginx, systemd, CI&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;Documented territory&lt;/td&gt;
&lt;td&gt;$0.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/advanced-ai-development-workflows" rel="noopener noreferrer"&gt;13&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Power moves — built nothing&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Nothing billed&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/ai-development-cost-retrospective" rel="noopener noreferrer"&gt;14&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The retro&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Nothing billed&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Season two · From Prompt to Polish&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What it built&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;th&gt;Added&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/ai-ui-design-escaping-the-slop" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Before shots, four directions, a veto&lt;/td&gt;
&lt;td&gt;Opus&lt;/td&gt;
&lt;td&gt;Divergence is a thinking task&lt;/td&gt;
&lt;td&gt;$0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/design-tokens-with-ai-blazor" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Tokens, dark mode, Bootstrap deleted&lt;/td&gt;
&lt;td&gt;Opus; Sonnet for the CSS&lt;/td&gt;
&lt;td&gt;Structure and taste&lt;/td&gt;
&lt;td&gt;$0.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/screenshot-loop-ai-design" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The screenshot harness&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;Bread-and-butter code&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/designing-booking-flow-phone-ux" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Booking flow and the ticket&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;Execution on a settled system&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/kiosk-touch-ux-design" rel="noopener noreferrer"&gt;5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Kiosk: code entry, success takeover&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;Execution on a settled system&lt;/td&gt;
&lt;td&gt;$0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/waiting-room-board-signage-ux" rel="noopener noreferrer"&gt;6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Board as signage&lt;/td&gt;
&lt;td&gt;Opus&lt;/td&gt;
&lt;td&gt;"What is this surface?"&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/staff-pages-pro-tool-ux" rel="noopener noreferrer"&gt;7&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Staff pages, the WAITED column&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;Execution on a settled system&lt;/td&gt;
&lt;td&gt;$0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/ai-accessibility-audit-blazor" rel="noopener noreferrer"&gt;8&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Accessibility audit and fixes&lt;/td&gt;
&lt;td&gt;Opus&lt;/td&gt;
&lt;td&gt;Adversarial reading&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/micro-interactions-css-blazor" rel="noopener noreferrer"&gt;9&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Motion that explains change&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;Execution on settled rules&lt;/td&gt;
&lt;td&gt;$0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/ai-ui-design-retrospective" rel="noopener noreferrer"&gt;10&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The retro&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Nothing billed&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Season three · From Prompt to Pocket&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What it built&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;th&gt;Added&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-companion-app-plan" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The plan and a smoke build&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Rides inside Part 2&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-shared-ui-three-hosts" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Contracts, shared RCL, two hosts, insets&lt;/td&gt;
&lt;td&gt;Opus; Sonnet for the stylesheet&lt;/td&gt;
&lt;td&gt;Architecture and a diagnosis&lt;/td&gt;
&lt;td&gt;$1.30 (with Part 1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-app-api-for-blazor-server-backend" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The API, the client, the Visit screen&lt;/td&gt;
&lt;td&gt;Sonnet; Opus for one call&lt;/td&gt;
&lt;td&gt;Plumbing on settled rules&lt;/td&gt;
&lt;td&gt;$0.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/signalr-in-maui-blazor-hybrid" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;SignalR on the phone, lifecycle, reconnect&lt;/td&gt;
&lt;td&gt;Opus; Sonnet for the page&lt;/td&gt;
&lt;td&gt;Connection semantics&lt;/td&gt;
&lt;td&gt;$0.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-haptics-local-notifications" rel="noopener noreferrer"&gt;5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Haptics, notifications, the Windows dead end&lt;/td&gt;
&lt;td&gt;Opus; Sonnet for the platform bodies&lt;/td&gt;
&lt;td&gt;Product decisions&lt;/td&gt;
&lt;td&gt;$0.80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/firebase-push-notifications-maui-android" rel="noopener noreferrer"&gt;6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Firebase push to a closed app&lt;/td&gt;
&lt;td&gt;Opus; Sonnet for the bindings&lt;/td&gt;
&lt;td&gt;Stopped-state semantics&lt;/td&gt;
&lt;td&gt;$0.80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-geolocation-maps" rel="noopener noreferrer"&gt;7&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Geolocation and maps&lt;/td&gt;
&lt;td&gt;Sonnet; Opus for two things&lt;/td&gt;
&lt;td&gt;Essentials did the work&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-qr-scanner-zxing-blazor-hybrid" rel="noopener noreferrer"&gt;8&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;QR on the ticket, the native scanner&lt;/td&gt;
&lt;td&gt;Opus for two judgments; Sonnet for the rest&lt;/td&gt;
&lt;td&gt;A native page over the WebView&lt;/td&gt;
&lt;td&gt;$0.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-offline-preferences-securestorage" rel="noopener noreferrer"&gt;9&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Connectivity, two-tier storage, the cached visit&lt;/td&gt;
&lt;td&gt;Sonnet; Opus for two decisions&lt;/td&gt;
&lt;td&gt;Plumbing&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-windows-desktop" rel="noopener noreferrer"&gt;10&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A desk-sized window, one breakpoint&lt;/td&gt;
&lt;td&gt;Sonnet&lt;/td&gt;
&lt;td&gt;CSS and a window size&lt;/td&gt;
&lt;td&gt;$0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-release-build-signing-ci" rel="noopener noreferrer"&gt;11&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Signed Android, Windows publish, CI&lt;/td&gt;
&lt;td&gt;Sonnet; Opus for what not to fake&lt;/td&gt;
&lt;td&gt;Toolchain plumbing&lt;/td&gt;
&lt;td&gt;$0.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-retrospective" rel="noopener noreferrer"&gt;12&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The retro&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Nothing billed&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Add it up by lead model and the split is even in count and uneven in cost. &lt;strong&gt;Fifteen Opus-led parts cost $10.10&lt;/strong&gt;, about $0.67 each. &lt;strong&gt;Fifteen Sonnet-led parts cost $6.90&lt;/strong&gt;, about $0.46 each — $0.49 if you leave out season one's setup part, which cost effectively nothing. Six parts have no reading of their own. So the thinking model's parts cost about half as much again, and less of that gap than you'd guess is the price list: the Opus parts were also the ones with the long design conversations, the failed first attempts and the most screenshots.&lt;/p&gt;

&lt;p&gt;One honesty note about that arithmetic. The log records one reading per part, not one per model. Where a part used both — most of season three did — the whole reading is counted under the model its 🧠 box leads with, the one that made the part's defining call. That is a judgment, and season three's scanner part is the closest one.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 &lt;strong&gt;The rule, unchanged for 36 parts:&lt;/strong&gt; match the spend to the consequence of error, not to the difficulty of the typing. Season one put it as "where mistakes compound"; season two as "what &lt;em&gt;is&lt;/em&gt; this surface?"; season three as "semantics — foreground versus background versus stopped." Same rule, three axes. It never needed revising, and the table above is the receipt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What caught the AI's mistakes
&lt;/h2&gt;

&lt;p&gt;The three retros keep a mistake table each: eight rows holding 14 mistakes for season one, 6 for season two, and 44 rows for season three, one of which is an honest "no code catch" rather than a mistake. The later two tables record what caught each one; season one's has no such column, so its 14 are classified here from the descriptions. Tallied together:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What caught each of the 63 logged mistakes, by season&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Caught by&lt;/th&gt;
&lt;th&gt;Season one&lt;/th&gt;
&lt;th&gt;Season two&lt;/th&gt;
&lt;th&gt;Season three&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A screenshot&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;21&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A human reading or looking&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;19&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The compiler or a failing build&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running it: the app, a log, the emulator&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A test&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mistakes logged&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;43&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;63&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The honest reading is that the tools people assume catch AI mistakes caught the fewest. The compiler and the build found ten of sixty-three, and most were fixed in minutes — &lt;code&gt;CS0542&lt;/code&gt; twice, an ambiguous &lt;code&gt;View&lt;/code&gt;, a DLL lock, a runtime pack that doesn't exist. Tests found four, and three of those were the test being wrong rather than the code. The two catchers that found forty of the sixty-three were a screenshot and a person. The screenshot loop found a sidebar on a TV, an error banner on every page, a status bar the wrong color, a phone that had quietly given up reconnecting — things no diff can show. A person found the rest, from a spec that invented SMS, to a palette that was a near-twin of the author's other project, to a captured desktop that should never have been captured. Season one's shape is different for a reason: with no screenshot loop yet, its catches came from reading code against a spec, which is why "a human" carries nine of its fourteen.&lt;/p&gt;

&lt;p&gt;Two fairness notes. Tests look weak in this tally and were the strongest thing in the trilogy: the nine written in season one stayed green through a redesign that touched every page, and the 28 at the end are why a phone, a desk and a browser could share one set of rules without anyone re-checking them by hand. Catching mistakes and preventing them are different jobs. And nothing in the table was free: screenshots are image tokens, and the person is not on the meter at all — which is the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the money did not buy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Human time is not on the meter.&lt;/strong&gt; Say it plainly, because the $17 headline invites the wrong conclusion. The meter counts tokens; it does not count reading every diff before accepting it, pasting a schema into a review, judging four art directions, vetoing a palette, toggling airplane mode, reading a vibrator log, or looking at a screenshot before writing its caption. Season one estimated its own wall-clock at roughly an hour of AI-assisted building against two to three days by hand — and even that hour sits beside the review time the retro called "the real cost," like the twenty minutes of human reading that &lt;a href="https://www.coder000.com/post/scaffolding-blazor-server-with-ai" rel="noopener noreferrer"&gt;Part 5&lt;/a&gt; said was the most expensive thing in it. Seasons two and three never logged hours at all; their retros say where the time went instead: "most of it is the looking," and "the meter bought code; the proving is where the season went." If you want a real cost per part, add your own hours to the $0.47.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The bench.&lt;/strong&gt; An Android emulator, a Windows desktop, a free-tier Firebase project, a PostgreSQL in Testcontainers, a Playwright harness that photographs every surface, and a screenshot script that learned — the hard way, twice — to refuse a frame it doesn't own. None of that is a token, and season three's retro is clear that the bench, not the model, is where that season went.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three things the bench couldn't prove.&lt;/strong&gt; Windows toast notifications never displayed: registration succeeds, the setting says Enabled, and no banner appears, because an unpackaged app has no identity and the series declined to fake an MSIX. No on-device QR decode was photographed, because the emulator never rendered its posters; the chain is proven by a test instead — everything except the lens. And Google Maps launched and crashed on the emulator after the directions intent resolved. Season three's retro keeps all three together, and $7.20 closed none of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was never built.&lt;/strong&gt; iOS compiles and was never built, for want of a Mac. Patient accounts and SMS, which the spec said no to in Part 3 and kept saying no to. A packaged Windows build. Anything a real phone does after the emulator's Maps died.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The judgment.&lt;/strong&gt; The most valuable lines in the trilogy were typed by a person before the model started, and cost nothing: the spec's non-goals; the rule that a test is written from the spec, never from the code; "who uses this surface, and what state do they find it in?"; "a second front door must not grow a second set of rules"; "stale means read-only"; "fake nothing." Each would have shipped as something plausible and worse without it. The dollars bought drafts; those sentences are why the drafts became an app.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the meter is measured
&lt;/h2&gt;

&lt;p&gt;Every retro carries the same footnote under its bill, with its own season's number in it. Here it is exactly as season three's retro prints it — the 🧠 boxes it refers to are the per-part model picks summarized in the table above:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How the meter is measured: the build's actual token consumption priced at the API list rates of the models used (the 🧠 boxes name them). On a subscription plan your bill won't literally say $7.20 — but the tokens burned are the same, so it's the honest apples-to-apples number.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two details that follow from it. The tokens include every image the model looked at, which is why screenshot-heavy parts cost more than their code would suggest. And every reading was written down at ten-cent precision as its part landed, which is why every number in this post wears a ≈.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How much does it cost to build an app with Claude Code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For ClinicLive it was about $17 of API spend across 36 parts: $6.20 for a Blazor Server app with PostgreSQL, SignalR and tests, $3.60 for a complete redesign, and $7.20 for a .NET MAUI companion app on Android, Windows and the web. That is actual token consumption priced at API list rates; the human time spent reviewing and testing is not included.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Opus or Sonnet cheaper for AI-assisted development?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this build the fifteen Sonnet-led parts averaged about $0.46 each and the fifteen Opus-led parts about $0.67, but the choice was made by consequence of error, not price: Opus for specs, schemas, real-time semantics, diagnosis and reviews; Sonnet for scaffolding, CRUD, tests, CSS and documented toolchains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What catches the mistakes in AI-generated code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Across 63 logged mistakes in three seasons, screenshots caught 21, a human reading or looking caught 19, the compiler or a failing build caught 10, running the app caught 8, tests caught 4 and CI caught 1. The compiler and tests are necessary, but most of the catching was done by looking at the running result and by a person.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does the $17 not include?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Human time: reading every diff, reviewing screenshots, running an emulator and a device bench, and deciding what correct means. It also leaves out the hardware and accounts used, three things the bench could not prove, and an iOS build that was never made.&lt;/p&gt;

&lt;p&gt;If you want the stories behind the numbers, the three retros have them: &lt;a href="https://www.coder000.com/post/ai-development-cost-retrospective" rel="noopener noreferrer"&gt;what ClinicLive really cost&lt;/a&gt;, &lt;a href="https://www.coder000.com/post/ai-ui-design-retrospective" rel="noopener noreferrer"&gt;what a redesign really costs&lt;/a&gt;, and &lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-retrospective" rel="noopener noreferrer"&gt;what a companion app really costs&lt;/a&gt;. And each season starts where its Part 1 does: &lt;a href="https://www.coder000.com/post/ai-assisted-development-with-claude" rel="noopener noreferrer"&gt;From Prompt to Production&lt;/a&gt;, &lt;a href="https://www.coder000.com/post/ai-ui-design-escaping-the-slop" rel="noopener noreferrer"&gt;From Prompt to Polish&lt;/a&gt;, and &lt;a href="https://www.coder000.com/post/maui-blazor-hybrid-companion-app-plan" rel="noopener noreferrer"&gt;From Prompt to Pocket&lt;/a&gt;. Thirty-six parts, one app, about seventeen dollars — and a great deal of looking that no meter ever saw.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.coder000.com/post/what-building-a-real-app-with-ai-actually-cost" rel="noopener noreferrer"&gt;coder000.com&lt;/a&gt;. It sums up the ClinicLive trilogy — &lt;a href="https://www.coder000.com/series/from-prompt-to-production" rel="noopener noreferrer"&gt;From Prompt to Production&lt;/a&gt;, &lt;a href="https://www.coder000.com/series/from-prompt-to-polish" rel="noopener noreferrer"&gt;From Prompt to Polish&lt;/a&gt; and &lt;a href="https://www.coder000.com/series/from-prompt-to-pocket" rel="noopener noreferrer"&gt;From Prompt to Pocket&lt;/a&gt; — read the whole series there.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>dotnet</category>
      <category>productivity</category>
      <category>claude</category>
    </item>
  </channel>
</rss>
