<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
  <link href="https://www.john-rush.com/feed.xml" rel="self" type="application/atom+xml" />
  <link href="https://www.john-rush.com/" rel="alternate" type="text/html" />
  <id>https://www.john-rush.com/</id>
  <title type="html">John Rush’s Blog</title>
  <subtitle>Tech blog about software development and other interesting topics</subtitle>
  <updated>2026-10-11T01:44:47+00:00</updated>
  <author>
    <name>John Rush</name>
  </author>

  
  
    
    
    <entry>
      <title type="html">Finding the Bottlenecks in Agentic Development</title>
      <link href="https://www.john-rush.com/posts/finding-agentic-development-bottlenecks-20260917.html" rel="alternate" type="text/html" title="Finding the Bottlenecks in Agentic Development" />
      <id>https://www.john-rush.com/posts/finding-agentic-development-bottlenecks-20260917.html</id>
      <published>2026-09-17T00:00:00+00:00</published>
      <updated>2026-09-17T00:00:00+00:00</updated>
      <content type="html" xml:base="https://www.john-rush.com/posts/finding-agentic-development-bottlenecks-20260917.html">&lt;h1 id=&quot;finding-the-bottlenecks-in-agentic-development&quot;&gt;Finding the Bottlenecks in Agentic Development&lt;/h1&gt;

&lt;p&gt;&lt;img srcset=&quot;../images/posts/header-300w.png 300w,              ../images/posts/header-400w.png 400w,              ../images/posts/header-600w.png 600w&quot; sizes=&quot;(max-width: 400px) 300px,             (max-width: 600px) 400px,             600px&quot; src=&quot;../images/posts/header-600w.png&quot; alt=&quot;Agentic development bottleneck metrics&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Published: September 17, 2026&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’m building a new AI email client. I wanted to improve my agentic development process, so I started instrumenting it and looking for bottlenecks.&lt;/p&gt;

&lt;p&gt;The goal wasn’t to produce a perfect benchmark or a universal formula for building software with agents. I wanted enough visibility to make better decisions about where to spend tokens, when to parallelize work, and which models should handle which jobs.&lt;/p&gt;

&lt;h2 id=&quot;start-with-progress&quot;&gt;Start with Progress&lt;/h2&gt;

&lt;p&gt;First, I used programmatically verifiable benchmarks to define progress. There is a benchmark for opening emails, another for viewing attachments, another for getting new mail, and others for things like loading speed and software performance.&lt;/p&gt;

&lt;p&gt;I grouped those benchmarks into checkpoints: vertical slices of capability that I could build and validate as I went. Each checkpoint contained several workstreams. For the “read emails” checkpoint, for example, one workstream handled opening rich-text email while another handled attachments.&lt;/p&gt;

&lt;p&gt;This gave the agents a nicely bounded scope to build against. It also exposed an interesting dependency: the right size for a workstream depended on the model executing it. Smaller models like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luna&lt;/code&gt; did better with smaller workstreams, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;astra&lt;/code&gt; handled larger ones well.&lt;/p&gt;

&lt;h2 id=&quot;measure-what-matters&quot;&gt;Measure What Matters&lt;/h2&gt;

&lt;p&gt;To understand the impact of different approaches, I needed process metrics. Quality metrics are important, but they belong elsewhere. These metrics are about how the work gets done.&lt;/p&gt;

&lt;p&gt;The first metric is cost. I used API cost per 10,000 changed, committed tokens—&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$/10kCoT&lt;/code&gt;—to normalize across workstreams and checkpoints with different scopes. Like lines of code, 10kCoT has obvious flaws, but it was good enough for this use. Even though I’m on a subscription, using API pricing gave me a consistent way to compare models. Empirically, it also aligned well with how my weekly limits were being used.&lt;/p&gt;

&lt;p&gt;The next metric is generated versus committed tokens. This approximates how much model output it took to produce the code that was ultimately committed. We don’t expect every output token to become code: there are reasoning tokens, coordination between agents, reviews, status reports, and some amount of rework or mistakes. A lower ratio is not automatically better.&lt;/p&gt;

&lt;p&gt;The important thing is to balance generated tokens against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$/10kCoT&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luna&lt;/code&gt; is so cheap that even generating ten times as many tokens costs only about 20% as much as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;astra&lt;/code&gt;. It isn’t as simple as minimizing generated tokens. That strategy can lead you in the wrong direction.&lt;/p&gt;

&lt;p&gt;The final metric is active time versus wall-clock time. Active time measures how much wall-clock time agents are actually working. This helps identify where I might be holding things up and where parallelism could help.&lt;/p&gt;

&lt;p&gt;There are tradeoffs here too. In my runs, the smaller models generally required more rework and review cycles. That cost time, but I traded the time for lower cost. Sometimes it is better to spend tokens to get speed—especially when Tibo says there is a reset coming at the end of the day.&lt;/p&gt;

&lt;p&gt;&lt;img srcset=&quot;../images/posts/KPOI%20pane-300w.png 300w,              ../images/posts/KPOI%20pane-400w.png 400w,              ../images/posts/KPOI%20pane-600w.png 600w&quot; sizes=&quot;(max-width: 400px) 300px,             (max-width: 600px) 400px,             600px&quot; src=&quot;../images/posts/KPOI%20pane-600w.png&quot; alt=&quot;KPI pane showing cost, token efficiency, and active-time metrics&quot; /&gt;&lt;/p&gt;

&lt;p&gt;These metrics let me make thoughtful tradeoffs instead of guessing at where the bottleneck is.&lt;/p&gt;

&lt;h2 id=&quot;how-it-went&quot;&gt;How It Went&lt;/h2&gt;

&lt;p&gt;The main resource constraint I’m facing is that I’m token-poor. I have a single Pro account, so I want to get as much productive use out of it as possible.&lt;/p&gt;

&lt;p&gt;On the early checkpoints, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;astra&lt;/code&gt; handled implementation, and it was expensive. CP-17 cost $23.59 per 10kCoT. I moved to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terra&lt;/code&gt; as the implementer and kept &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;astra&lt;/code&gt; as the reviewer. That lowered CP-7’s cost to $15.60 per 10kCoT—a significant improvement—but the review was still expensive. In WS-19’s case, the review cost twice as much as the implementation itself.&lt;/p&gt;

&lt;p&gt;&lt;img srcset=&quot;../images/posts/WS19-300w.png 300w,              ../images/posts/WS19-400w.png 400w,              ../images/posts/WS19-600w.png 600w&quot; sizes=&quot;(max-width: 400px) 300px,             (max-width: 600px) 400px,             600px&quot; src=&quot;../images/posts/WS19-600w.png&quot; alt=&quot;WS-19 cost breakdown showing review cost relative to implementation&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That suggested a new model: have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terra&lt;/code&gt; perform a first review-and-fix pass, followed by a final review with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;astra&lt;/code&gt;. This reduced WS-34’s cost to $9.27 per 10kCoT. Even with multiple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terra&lt;/code&gt; reviews, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terra&lt;/code&gt; accounted for only 58% of the total review cost, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;astra&lt;/code&gt; accounted for 42%.&lt;/p&gt;

&lt;p&gt;&lt;img srcset=&quot;../images/posts/WS34-300w.png 300w,              ../images/posts/WS34-400w.png 400w,              ../images/posts/WS34-600w.png 600w&quot; sizes=&quot;(max-width: 400px) 300px,             (max-width: 600px) 400px,             600px&quot; src=&quot;../images/posts/WS34-600w.png&quot; alt=&quot;WS-34 model and cost comparison&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In a couple of cases, though, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terra&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luna&lt;/code&gt; implementers did not complete all of the required benchmarks. I was spending expensive review tokens on incomplete work.&lt;/p&gt;

&lt;p&gt;So I added a simple completeness-check step with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luna&lt;/code&gt;. That step looked only for benchmark completeness; it did not evaluate correctness. This drove the cost of the next workstream down to $2.11 per 10kCoT, with the completeness review costing only $0.19.&lt;/p&gt;

&lt;p&gt;&lt;img srcset=&quot;../images/posts/WS-48-300w.png 300w,              ../images/posts/WS-48-400w.png 400w,              ../images/posts/WS-48-600w.png 600w&quot; sizes=&quot;(max-width: 400px) 300px,             (max-width: 600px) 400px,             600px&quot; src=&quot;../images/posts/WS-48-600w.png&quot; alt=&quot;WS-48 model and cost comparison&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Cheap checks that save expensive work are a classic system-design trick.&lt;/p&gt;

&lt;p&gt;I was still seeing my weekly limit get consumed fairly quickly, so I looked at what was still costing a lot. In this case, it was the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;astra&lt;/code&gt; coordinator spending expensive tokens acting as a heartbeat. CP-7’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;astra&lt;/code&gt; coordination cost $1.86 per 10kCoT, while CP-9’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terra&lt;/code&gt; coordinator cost about $0.15 per 10kCoT—even though it handled about three times as much coordination.&lt;/p&gt;

&lt;h2 id=&quot;the-next-bottleneck&quot;&gt;The Next Bottleneck&lt;/h2&gt;

&lt;p&gt;The best optimization is not to do the work in the first place. So I dug into why there was so much coordination.&lt;/p&gt;

&lt;p&gt;Some architectural assumptions required the work to be serialized. A rearchitecture that removed those assumptions and built stronger, more independent components would allow more concurrency without increasing coordination proportionally.&lt;/p&gt;

&lt;p&gt;Independent components would let me explore more complex agent-swarm topologies, apply more compute to the problem, and—most importantly—measure whether those approaches actually help or merely burn dollars.&lt;/p&gt;
</content>
      <summary type="html">Using process instrumentation to find and remove bottlenecks in an agentic development workflow.</summary>
      
      
        
        <media:thumbnail url="https://www.john-rush.com/images/posts/header-600w.png" />
        <media:content medium="image" url="https://www.john-rush.com/images/posts/header-600w.png" />
      
      <author><name>John Rush</name></author>
    </entry>
  
    
    
    <entry>
      <title type="html">A Nap, a Walk, and a USB Stick</title>
      <link href="https://www.john-rush.com/posts/nap-walk-usb-stick-20260730.html" rel="alternate" type="text/html" title="A Nap, a Walk, and a USB Stick" />
      <id>https://www.john-rush.com/posts/nap-walk-usb-stick-20260730.html</id>
      <published>2026-08-03T00:00:00+00:00</published>
      <updated>2026-08-03T00:00:00+00:00</updated>
      <content type="html" xml:base="https://www.john-rush.com/posts/nap-walk-usb-stick-20260730.html">&lt;h1 id=&quot;a-nap-a-walk-and-a-usb-stick&quot;&gt;A Nap, a Walk, and a USB Stick&lt;/h1&gt;

&lt;p&gt;&lt;img srcset=&quot;../images/posts/nap-walk-usb-stick-header-300w.jpg 300w,              ../images/posts/nap-walk-usb-stick-header-400w.jpg 400w,              ../images/posts/nap-walk-usb-stick-header.jpg 600w&quot; sizes=&quot;(max-width: 400px) 300px,             (max-width: 600px) 400px,             600px&quot; src=&quot;../images/posts/nap-walk-usb-stick-header.jpg&quot; alt=&quot;A painted still life of a router, Wi-Fi access point, iPad, USB stick, walking shoes, stroller, grocery bag, and baby blanket&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Published: August 3, 2026&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We recently welcomed a new daughter to our family. During the late nights I’ve been streaming a few games on my iPad. Unfortunately, sometimes the stream would freeze and stutter. Something was up.&lt;/p&gt;

&lt;h2 id=&quot;why-cant-you-just-do-it&quot;&gt;Why Can’t You Just Do It?&lt;/h2&gt;

&lt;p&gt;After I put her down for a quick nap I asked Codex:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Okay, I am trying to debug why my internet is so slow. Can you look at my network configuration? I have a pfSense router on 192.168.1.1, and I can give you the username/password for it (or you can get it from the 1Password CLI, item [REDACTED]). I also have a Wi-Fi router that I’m using, but I can also hook this computer up to Ethernet. I notice when I’m streaming a game on my iPad’s Wi-Fi, I get lots of freezes and drops, whereas that didn’t happen on a different network on vacation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Codex started by mapping the interfaces and routes using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scutil --nwi&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;route -n get default&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;networksetup -listallhardwareports&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifconfig -a&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ipconfig&lt;/code&gt;. It inspected the radio with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system_profiler SPAirPortDataType&lt;/code&gt; and established baselines with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traceroute&lt;/code&gt;. Then Codex logged into pfSense via the HTTP admin console using a script that read the secret from 1Password CLI (Codex never read the password directly). It found both WAN and LAN are at 1 Gb/s full duplex with no errors or collisions.&lt;/p&gt;

&lt;p&gt;Next it did some load testing using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping -c 250 -i 0.1 192.168.1.1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;networkQuality -v -s -I en1 -M 35&lt;/code&gt;. On the AP’s original channel it measured 59.9 Mb/s, 70.1 ms average AP latency, 12.8% packet loss, and 544 ms loaded responsiveness. Brutal.&lt;/p&gt;

&lt;p&gt;It then located the Wi-Fi access point using pfSense’s ARP and DHCP pages, along with the Mac’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arp&lt;/code&gt; table. Codex fingerprinted the access point as a Ubiquiti via its ssh host key and as a UAP-AC-Pro using Ubiquiti’s UDP discovery protocol. Impressive. Then it narrowed the tests just to the AP and found the packet loss was happening on the first hop, before pfSense or the Internet.&lt;/p&gt;

&lt;p&gt;These are exactly the diagnostics I’d do myself, using the same tools. Well, I don’t have to ssh fingerprint the access point. I can just look at it. But I’d have to spend a bunch of time looking up commands because I can never remember the exact incantations.&lt;/p&gt;

&lt;p&gt;Codex suggested I try changing the AP to channel 44 at 40 MHz through the UniFi app. I made the change and had Codex retest: 32.9 Mb/s, 221 ms average AP latency, 13.3% packet loss, and 1,285 ms loaded responsiveness. Worse. I then gave it the 1Password item for the AP. It logged into the AP over ssh and saw thousands of retries and 14k transmission-queue drops. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ethtool&lt;/code&gt; showed the AP’s wired uplink was clean at 1 Gb/s full duplex.&lt;/p&gt;

&lt;p&gt;That’s when I realized my mistake. I need an &lt;em&gt;agent&lt;/em&gt; to do this work. I can’t be fiddling with all this and taking care of a baby. This is a perfectly agent-shaped problem (action + verification).  So I asked Codex the most important question. “Why can’t you just do it?”&lt;/p&gt;

&lt;p&gt;Again Codex leapt into action. It ran another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system_profiler SPAirPortDataType&lt;/code&gt; scan and found no nearby networks in the DFS block around channel 100. It then connected to the AP via ssh and made sure it supported channel 100. It then changed the AP’s channel temporarily, tested, and got 144.9 Mb/s, 29.7 ms average AP latency, 2.5% packet loss, and 142 ms loaded responsiveness. That was 4.4x the throughput of channel 44 and roughly a 9x improvement in loaded responsiveness. Awesome.&lt;/p&gt;

&lt;p&gt;Codex then persisted the change and verified the configuration.&lt;/p&gt;

&lt;p&gt;This is the same way I’d make the change too. Scan for open channels, test, verify. Very nice. And it all happened during a nap.&lt;/p&gt;

&lt;h2 id=&quot;great-what-else-should-i-consider&quot;&gt;Great. What Else Should I Consider?&lt;/h2&gt;

&lt;p&gt;Naturally, I asked Codex the obvious next question about my home network: “Great. What else should I consider?” I know the pfSense router is hopelessly out of date. It’s been ages and I’ve just never wanted to spend time on it. Instead, I decided to let Codex have a crack at it while we walked to PCC.&lt;/p&gt;

&lt;p&gt;Codex first considered updating the AP’s firmware based on the version it saw earlier. It did some research and decided not to install it because the fixes included in the update I didn’t need and there were reports online about regressions on my legacy hardware, but it did find a more important issue.&lt;/p&gt;

&lt;p&gt;My router was running pfSense 2.4.4-p3 and that is horrifically out of date. I’ve known this for a while and I thought, why not? Let’s see how this goes. Codex and I chatted and we landed on a fresh install of OPNsense. I told Codex to figure out the install and all the configuration we needed.&lt;/p&gt;

&lt;p&gt;It did some archaeology on the old pfSense and found an old OpenVPN configuration, some unused VLANs and a broken &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pfBlockerNG-devel&lt;/code&gt; installation. I told Codex that we didn’t need any of that anymore. Let’s start fresh. Codex got to work downloading OPNsense, getting the configuration documented and backing up the old pfSense install.&lt;/p&gt;

&lt;p&gt;Codex did a much more thorough job than I would have. I would have just popped in a USB installer and yoloed. Instead, Codex sshed into the pfSense box, created an encrypted backup, and inspected and validated the hardware to make sure the OPNsense installation goes swimmingly. It did suggest replacing the 9 year old SSD based on power-on hours, but I pushed back. Codex then automatically dug into the SMART data and things looked fine. I guess we can’t quite setup the drinking bird to just press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt; yet lol.&lt;/p&gt;

&lt;p&gt;It got everything ready, but the actual install had to wait for us to finish the walk. Only I could plug in the USB stick and boot the router. But once that was done Codex could ssh in and finish the installation. It had me unplug the USB and reboot the router, and everything came right up.&lt;/p&gt;

&lt;p&gt;After that, Codex finished configuring firewalls, setting up and validating Quad9. It also did ad blocking with OISD via Unbound to replace my old and broken &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pfBlockerNG-devel&lt;/code&gt;. It also validated that was working and the list updates were coming in.&lt;/p&gt;

&lt;p&gt;I asked Codex once more if there was anything else. It noticed that we were double NAT’d behind my C5500XK SmartNID. It backed up the configuration and put the SmartNID in Transparent Bridging with Tagged-201 mode. OPNsense had retained a stale private WAN lease so it reloaded WAN DHCP to get a new public lease and verified that the private 192.168.0.1 hop was gone. I was impressed that Codex fixed all that hands-off.&lt;/p&gt;

&lt;p&gt;Of course, I had to do the final test. That night I streamed games on the iPad without any issues.&lt;/p&gt;

&lt;p&gt;None of this is particularly challenging. This is all stuff I’ve done before. What’s exciting is how easy it is. I tell agents to fix stuff, grant permissions, add a little judgment, and plug in a USB stick. My home network gets upgraded while I spend time with my new daughter.&lt;/p&gt;
</content>
      <summary type="html">Codex fixed my freezing Wi-Fi during a nap, then migrated my router from pfSense to OPNsense during a walk with my daughter</summary>
      
      
        
        <media:thumbnail url="https://www.john-rush.com/images/posts/nap-walk-usb-stick-header.jpg" />
        <media:content medium="image" url="https://www.john-rush.com/images/posts/nap-walk-usb-stick-header.jpg" />
      
      <author><name>John Rush</name></author>
    </entry>
  
    
    
    <entry>
      <title type="html">25 Years of Eggs</title>
      <link href="https://www.john-rush.com/posts/eggs-25-years-20260219.html" rel="alternate" type="text/html" title="25 Years of Eggs" />
      <id>https://www.john-rush.com/posts/eggs-25-years-20260219.html</id>
      <published>2026-02-23T00:00:00+00:00</published>
      <updated>2026-02-23T00:00:00+00:00</updated>
      <content type="html" xml:base="https://www.john-rush.com/posts/eggs-25-years-20260219.html">&lt;h1 id=&quot;25-years-of-eggs&quot;&gt;25 Years of Eggs&lt;/h1&gt;

&lt;p&gt;&lt;img srcset=&quot;../images/posts/eggs-header-300w.jpg 300w,              ../images/posts/eggs-header-400w.jpg 400w,              ../images/posts/eggs-header.jpg 600w&quot; sizes=&quot;(max-width: 400px) 300px,             (max-width: 600px) 400px,             600px&quot; src=&quot;../images/posts/eggs-header.jpg&quot; alt=&quot;25 Years of Eggs&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Published: February 23, 2026&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Everyone needs a rewarding hobby. I’ve been scanning all of my receipts since 2001. I never typed in a single price - just kept the images. I figured someday the technology to read them would catch up, and the data would be interesting.&lt;/p&gt;

&lt;p&gt;This year I tested it. Two AI coding agents, 11,345 receipts. I started with eggs. If you can track one item across 25 years of garbled thermal prints, OCR failures, and folder typos, you can track anything.&lt;/p&gt;

&lt;p&gt;14 days. 1.6 billion tokens. 589 egg receipts found. Here’s what the data says.&lt;/p&gt;

&lt;div id=&quot;egg-price-chart&quot;&gt;&lt;/div&gt;

&lt;div id=&quot;egg-map&quot;&gt;&lt;/div&gt;

&lt;div id=&quot;egg-counter&quot;&gt;&lt;/div&gt;

&lt;div id=&quot;egg-inflation&quot;&gt;&lt;/div&gt;

&lt;div id=&quot;egg-heatmap&quot;&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;Here’s what I typed on Day 1:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ok so let’s make a project plan. In the ~/Records/ we have a ton of receipts. Many are pdf/image/etc. I want to go through and extract the actual content of the receipts to find how much we spend on eggs. Receipts are notoriously terrible to OCR, so we might need to do something more advanced.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Codex explored my file system, found two existing SQLite databases I’d forgotten about, discovered 11,345 receipts across PDFs, emails, and images, and came back with a project plan. I said “write this out to a plan.md please.” It did. We were building within the hour.&lt;/p&gt;

&lt;p&gt;The whole thing took 14 days. Maybe 15 hours of me actually at the keyboard - short bursts of direction-giving separated by long stretches of the agents just running. Codex ran 15 interactive sessions. Claude handled 10.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/eggs/eggs-pipeline.svg&quot; alt=&quot;Receipt processing pipeline diagram&quot; style=&quot;width:100%;margin:1.5em 0&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;shades-of-white&quot;&gt;Shades of White&lt;/h3&gt;

&lt;p&gt;The oldest receipts were flatbed scans - multiple receipts per page, random orientations, white paper on a white scanner bed. Codex and I tried seven classical CV approaches to find receipt boundaries. Edge detection. Adaptive thresholding. Contour analysis. Morphological operations. Watershed segmentation. Template matching. A grid-based decomposition I pitched as “a classic HackerRank problem.”&lt;/p&gt;

&lt;p&gt;None of them worked. The core issue: receipts are white and so is the scanner bed. I started calling it the “shades of white” problem. The cleverest attempt was inspired by removing tourists from landmark photos - stack all scans, compute the median pixel at each position, subtract to reveal edges. I thought that one was going to work. Best F1: 0.302.&lt;/p&gt;

&lt;p&gt;We also threw macOS Vision OCR at it (via a Swift script Codex wrote on the fly), Tesseract, several other tools. I was starting to think the flatbed scans might just be a loss. Then I tried Meta’s SAM3.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/eggs/sam3.jpg&quot; alt=&quot;Two receipts on a white flatbed scan - white paper on white background. Green boxes show SAM3 finding the boundaries.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One API call with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text=&quot;receipt&quot;&lt;/code&gt;. 0.92-0.98 confidence on every boundary. Four seconds per scan. 1,873 receipts from 760 multi-receipt pages. Seven approaches in hours; SAM3 in an afternoon.&lt;/p&gt;

&lt;h3 id=&quot;wait---you-already-know-the-answer&quot;&gt;Wait - You Already Know the Answer&lt;/h3&gt;

&lt;p&gt;Receipts land at random angles, and OCR needs them upright. We tried Tesseract’s orientation detection, macOS Vision OCR, Moondream 2 and 3 - each one better than the last but none reliable enough. Then I realized that every time I pasted a receipt into our Claude conversation to debug something, it was already reading the text perfectly. Rotated, faded, didn’t matter.&lt;/p&gt;

&lt;p&gt;Why am I building a rotation pipeline when the tool I’m talking to already solves this? So we sent all 11,345 receipts through Sonnet and Codex. Sometimes the answer is staring you right in the face.&lt;/p&gt;

&lt;h3 id=&quot;replacing-tesseract-overnight&quot;&gt;Replacing Tesseract Overnight&lt;/h3&gt;

&lt;p&gt;Halfway through the project, Tesseract was the weak link. It read “OAT MILK” as “OATH ILK.” It dropped decimals - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$4.37&lt;/code&gt; became &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$437&lt;/code&gt;. On old thermal prints it produced nothing at all. Codex opened 20 of the worst ones by hand and found that some weren’t even receipts. A family photo. A postcard. A greeting card. All filed under “Receipts.”&lt;/p&gt;

&lt;p&gt;I found PaddleOCR-VL - a 0.9B parameter vision-language model that runs locally on Apple Silicon. First test on a sample bank statement: clean, accurate text in 2.1 seconds. Tesseract was faster but dramatically noisier. Second test on a tall Fred Meyer receipt: disaster. The model entered a repetition loop, hallucinating “TILL YGRT” endlessly.&lt;/p&gt;

&lt;p&gt;The fix turned out to be simple - split tall receipts into slices. Dynamic slicing based on aspect ratio: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;num_slices = max(2, round(aspect_ratio / 1.5))&lt;/code&gt;. Five parallel shards ran overnight. GPU pegged at 100% for 10.8 hours. In the morning: 11,345 receipts OCR’d successfully. Cleaner text for every receipt in the archive.&lt;/p&gt;

&lt;p&gt;PaddleOCR-VL isn’t a Codex replacement - it can’t do structured extraction or follow instructions. It’s a better Tesseract. The real pipeline: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;receipt image → PaddleOCR-VL (local, clean text) → Codex/Claude (structured extraction)&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;twelve-hours-to-three&quot;&gt;Twelve Hours to Three&lt;/h3&gt;

&lt;p&gt;Once receipts were segmented, oriented, and OCR’d, they needed structured extraction - find the egg line items, pull prices and quantities.&lt;/p&gt;

&lt;p&gt;It started with regex. The models love regex. Keyword matching for “egg,” money patterns for prices. Heuristics found eggs in 25/25 positive samples with 0 false positives. Not bad. But on the full corpus, false negatives piled up - Fred Meyer abbreviated codes like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STO LRG BRUNN&lt;/code&gt;, Whole Foods truncated to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EDGS&lt;/code&gt;, OCR mangled “EGGS” into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LG EGO 12 CT&lt;/code&gt;. No regex catches these.&lt;/p&gt;

&lt;p&gt;So I told Codex “we have unlimited tokens, let’s use them all,” and we pivoted to sending every receipt through Codex for structured extraction. From that one sentence, Codex came back with a parallel worker architecture - sharding, health management, checkpointing, retry logic. The whole thing. When I ran out of tokens on Codex mid-run, it auto-switched to Claude and kept going. I didn’t ask it to do that. I didn’t know it had happened until I read the logs.&lt;/p&gt;

&lt;p&gt;But the runs kept crashing. Long CLI jobs died when sessions timed out. The script committed results at end-of-run, so early deaths lost everything. I watched it happen three times. On the fourth attempt I said “I would have expected we start a new process per batch.” That was the fix - one fresh process per batch, hard call cap, exit cleanly, resume from cache. Codex patched it, launched it in a tmux session, and the ETA dropped from 12 hours to 3. Not a hard fix. Just the kind of thing you know after you’ve watched enough overnight jobs die at 3 AM.&lt;/p&gt;

&lt;p&gt;11,345 receipts processed. The thing that was supposed to take all night finished before I went to bed.&lt;/p&gt;

&lt;h3 id=&quot;the-classifier-that-beat-its-own-ground-truth&quot;&gt;The Classifier That Beat Its Own Ground Truth&lt;/h3&gt;

&lt;p&gt;First I needed ground truth. I asked Claude to build me a labeling tool - keyboard-first, receipt image on the left, classification data on the right, arrow keys to navigate, single keypress to verdict. It built the whole Flask app in 22 minutes. I sat down and hand-labeled 375 receipts.&lt;/p&gt;

&lt;p&gt;Regex found 650 receipts mentioning “egg.” Against those 375 labels: 88% recall. The misses told the story - abbreviated codes, OCR garble, truncated descriptions. No keyword search catches &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STO LRG BRUNN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The fix: use those hand-labeled edge cases as few-shot examples in an LLM classifier. Twenty examples of what “eggs” looks like on a garbled thermal print from 2003. Batch 10 receipts per call. Eight parallel workers. Two hours. 11,345 receipts classified.&lt;/p&gt;

&lt;p&gt;Final accuracy: 99%+. Every supposed “miss” by the LLM turned out to be a mislabel in the ground truth. A bicycle shop receipt the old heuristic had flagged. A barcode-only scan. Egg noodles. The classifier was more correct than my labels.&lt;/p&gt;

&lt;p&gt;Then more QA. A second tool for eyeballing 497 weak images: Space for no-eggs, X for has-eggs. A third for data entry on 95 receipts with missing fields - numpad-optimized, auto-advancing. Four tools total, each built in minutes, each one I ground through by hand.&lt;/p&gt;

&lt;h3 id=&quot;quality&quot;&gt;Quality&lt;/h3&gt;

&lt;p&gt;So how good is the data? I pulled 372 random samples and checked them by hand. Initially: 96% correct. The errors were mostly garbled OCR on old scans. One was a hallucination - the pipeline fabricated egg data for a receipt that contained no eggs at all.&lt;/p&gt;

&lt;p&gt;Real-world data is messy:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Receipt folder typos spanning years: “Reciepts” (2016-2017) and “Recipts”&lt;/li&gt;
  &lt;li&gt;A receipt scanned backwards - Claude decoded the mirrored OCR character by character&lt;/li&gt;
  &lt;li&gt;Email receipts silently preferring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text/plain&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text/html&lt;/code&gt;, dropping pricing lines that only existed in the HTML part&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what made the quality good: every time I caught something, I could show the agents what to look for and they’d go fix it everywhere. I caught a store address hiding in OCR noise: “915 Ny 45th St” was 915 NW 45th St, Seattle. I showed them the pattern, they ran a recovery pass on 40 missing-location receipts - all 40 resolved.&lt;/p&gt;

&lt;h3 id=&quot;by-the-numbers&quot;&gt;By the Numbers&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt; &lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Wall clock time&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;14 days (Feb 8-22, 2026)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Hands-on time&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;15 hours&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Tokens consumed&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1.6 billion&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Estimated token cost&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;$1,591&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Confirmed egg receipts&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;589&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Total egg spend captured&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;$1,972&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Total eggs&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;8,604&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Codex and Claude are excellent at building tools and extracting structured data, but they couldn’t segment an image or replace an OCR engine. The right answer was a stack of specialized models - SAM3 for segmentation, PaddleOCR for text, Codex and Claude for everything else. I expected this, but it was worth trying the simple path first.&lt;/p&gt;

&lt;p&gt;These are the days of miracle and wonder. I can’t wait to see what 30 years of eggs looks like.&lt;/p&gt;

&lt;link rel=&quot;stylesheet&quot; href=&quot;../assets/eggs/eggs-styles.css&quot; /&gt;

&lt;script src=&quot;../assets/eggs/eggs-data.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;../assets/eggs/eggs-chart.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;../assets/eggs/eggs-map.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;../assets/eggs/eggs-counter.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;../assets/eggs/eggs-inflation.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;../assets/eggs/eggs-heatmap.js&quot;&gt;&lt;/script&gt;

</content>
      <summary type="html">Two AI agents, 11,345 receipts, and 1.6 billion tokens to find out what I&apos;ve spent on eggs since 2001.</summary>
      
      
        
        <media:thumbnail url="https://www.john-rush.com/images/posts/eggs-header.jpg" />
        <media:content medium="image" url="https://www.john-rush.com/images/posts/eggs-header.jpg" />
      
      <author><name>John Rush</name></author>
    </entry>
  
    
    
    <entry>
      <title type="html">Compounding Beats Roadmaps</title>
      <link href="https://www.john-rush.com/posts/compounding-beats-roadmaps-20260131.html" rel="alternate" type="text/html" title="Compounding Beats Roadmaps" />
      <id>https://www.john-rush.com/posts/compounding-beats-roadmaps-20260131.html</id>
      <published>2026-01-31T00:00:00+00:00</published>
      <updated>2026-01-31T00:00:00+00:00</updated>
      <content type="html" xml:base="https://www.john-rush.com/posts/compounding-beats-roadmaps-20260131.html">&lt;h1 id=&quot;compounding-beats-roadmaps&quot;&gt;Compounding Beats Roadmaps&lt;/h1&gt;

&lt;p&gt;&lt;img srcset=&quot;../images/posts/compounding-beats-roadmaps-header-300w.jpg 300w,              ../images/posts/compounding-beats-roadmaps-header-400w.jpg 400w,              ../images/posts/compounding-beats-roadmaps-header.jpg 600w&quot; sizes=&quot;(max-width: 400px) 300px,             (max-width: 600px) 400px,             600px&quot; src=&quot;../images/posts/compounding-beats-roadmaps-header.jpg&quot; alt=&quot;Compounding Beats Roadmaps&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Published: January 31, 2026&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;Service-heavy SaaS companies are facing an organizational challenge disguised as a technical one.&lt;/p&gt;

&lt;p&gt;These are products where a meaningful amount of value is created in use: through configuration, implementation, customer-specific workflows, and ongoing adaptation. They operate under real uncertainty. Customer needs change quickly, information is incomplete, and local context dominates outcomes.&lt;/p&gt;

&lt;p&gt;Think implementation-heavy enterprise platforms like Salesforce or Workday, vertical SaaS with deep industry configuration, or any product where customer success, implementation consultants, and solutions engineers create significant value post-sale.&lt;/p&gt;

&lt;p&gt;In that environment, the most important shift AI enables is organizational, not technical. It allows organizations to decentralize execution in a way that creates compounding learning and capability which outperforms traditional roadmap-driven development in high-uncertainty markets.&lt;/p&gt;

&lt;p&gt;In this post, a &lt;strong&gt;skill&lt;/strong&gt; is a reusable, versioned playbook or workflow. An &lt;strong&gt;agent&lt;/strong&gt; is the software that runs skills with customer context.&lt;/p&gt;

&lt;h2 id=&quot;ai-tools-without-organizational-change&quot;&gt;AI Tools Without Organizational Change&lt;/h2&gt;

&lt;p&gt;Today, many SaaS companies are adopting AI tools. What they aren’t changing are the underlying organizational constraints:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Who has decision rights&lt;/li&gt;
  &lt;li&gt;Where authority lives&lt;/li&gt;
  &lt;li&gt;How work flows through execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, what’s happening is this: AI lowers the cost of local reasoning and local work, but organizations keep execution centralized. The same approval chains, roadmaps, and ticket queues remain in place.&lt;/p&gt;

&lt;p&gt;The result is predictable:&lt;/p&gt;

&lt;p&gt;With AI, the queue moves faster - but not smarter.&lt;/p&gt;

&lt;p&gt;In service-heavy SaaS, where execution happens continuously in customer environments, centralized execution throttles the very advantage AI creates.&lt;/p&gt;

&lt;p&gt;Example: Your implementation team sees the same integration failure pattern across 15 enterprise customers. With AI, a consultant could build a diagnostic workflow and fix script in an afternoon. Instead, it goes into a ticket. Product reviews it in two weeks. Engineering prioritizes it next quarter. Six months later, the pattern is still being debugged manually, customer by customer. The capability to solve it was available from day one. The organizational structure prevented it.&lt;/p&gt;

&lt;h2 id=&quot;weve-seen-this-failure-mode-before&quot;&gt;We’ve Seen This Failure Mode Before&lt;/h2&gt;

&lt;p&gt;This problem isn’t unique to software. It appears any time organizations operate in environments with high uncertainty and rapid change.&lt;/p&gt;

&lt;p&gt;The clearest precedent comes from the military.&lt;/p&gt;

&lt;p&gt;Historically, many militaries relied on strict centralized command-and-control. Decisions were made far from the front lines, plans were detailed in advance, and lower-level units were expected to execute precisely. This worked in stable environments, but failed badly in fluid ones.&lt;/p&gt;

&lt;p&gt;Latency destroyed outcomes. By the time information traveled up the chain and orders came back down, reality had already changed.&lt;/p&gt;

&lt;h2 id=&quot;the-shift-to-mission-command&quot;&gt;The Shift to Mission Command&lt;/h2&gt;

&lt;p&gt;In response, modern military doctrine - most notably German &lt;em&gt;Auftragstaktik&lt;/em&gt; and later U.S. mission command - shifted away from centralized decision-making.&lt;/p&gt;

&lt;p&gt;Instead of issuing detailed instructions, leadership focused on:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Defining the objective&lt;/li&gt;
  &lt;li&gt;Communicating intent&lt;/li&gt;
  &lt;li&gt;Setting constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lower-level leaders are expected to decide how to achieve the objective based on real-time conditions.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A commander specifies the goal - secure a position, protect civilians, delay an advance - without prescribing exact maneuvers&lt;/li&gt;
  &lt;li&gt;A platoon leader encountering unexpected terrain or enemy movement adapts immediately, without waiting for approval&lt;/li&gt;
  &lt;li&gt;Units that wait for permission in these environments lose tempo and often fail&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Crucially, this model assumes something strong:&lt;/p&gt;

&lt;p&gt;Leaders at the edge are not just allowed to decide - they are required to decide.&lt;/p&gt;

&lt;p&gt;Micromanagement from afar consistently underperforms disciplined initiative at the point of contact.&lt;/p&gt;

&lt;h2 id=&quot;service-heavy-saas-now-faces-the-same-conditions&quot;&gt;Service-Heavy SaaS Now Faces the Same Conditions&lt;/h2&gt;

&lt;p&gt;Service-heavy SaaS organizations increasingly operate under similar dynamics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fast-moving markets&lt;/li&gt;
  &lt;li&gt;Incomplete or delayed information&lt;/li&gt;
  &lt;li&gt;Customers behaving in unexpected ways&lt;/li&gt;
  &lt;li&gt;Value created through ongoing human execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Customer success teams, implementation consultants, solutions engineers, and support staff often see real problems weeks or months before centralized planning cycles surface them.&lt;/p&gt;

&lt;p&gt;How has AI changed economically? Three ways:&lt;/p&gt;

&lt;p&gt;AI dropped the cost of drafting workable solutions. Frontline teams can now build reliable workflows and automations without waiting for engineering. What used to require a sprint now takes an afternoon.&lt;/p&gt;

&lt;p&gt;AI dropped the cost of iterating with full context. The person closest to the customer can try five approaches in a day and see which one actually works. Centralized teams working from abstracted requirements can’t move that fast.&lt;/p&gt;

&lt;p&gt;AI dropped the cost of packaging learning into reusable artifacts. A workflow that solves a problem can be described, shared, and adapted by other teams without rewriting from scratch. The knowledge compounds instead of living in someone’s head.&lt;/p&gt;

&lt;p&gt;The bottleneck shifted from engineering capacity to decision rights. Who’s allowed to ship? That’s the new constraint.&lt;/p&gt;

&lt;h2 id=&quot;centralize-intent-decentralize-execution&quot;&gt;Centralize Intent, Decentralize Execution&lt;/h2&gt;

&lt;p&gt;The split looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Centralized:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Strategy and vision&lt;/li&gt;
  &lt;li&gt;Intent and priorities&lt;/li&gt;
  &lt;li&gt;Guardrails and constraints&lt;/li&gt;
  &lt;li&gt;Primitives and platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Decentralized:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Decision-making&lt;/li&gt;
  &lt;li&gt;Adaptation in customer environments&lt;/li&gt;
  &lt;li&gt;Skill construction&lt;/li&gt;
  &lt;li&gt;Local product and configuration surface&lt;/li&gt;
  &lt;li&gt;Optimization for specific customer contexts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the direct analog of mission command:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Leadership sets intent&lt;/li&gt;
  &lt;li&gt;Constraints are explicit&lt;/li&gt;
  &lt;li&gt;Execution authority moves to the point of contact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI makes this economically viable for the first time in SaaS.&lt;/p&gt;

&lt;h2 id=&quot;how-organizational-compounding-actually-works&quot;&gt;How Organizational Compounding Actually Works&lt;/h2&gt;

&lt;p&gt;The mechanism is simple:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Real problem appears at the customer edge&lt;/li&gt;
  &lt;li&gt;Frontline team builds or updates a skill (a reusable workflow/playbook) using centralized primitives&lt;/li&gt;
  &lt;li&gt;It’s used immediately - validated or killed by actual outcomes&lt;/li&gt;
  &lt;li&gt;If it works, other teams discover and reuse it&lt;/li&gt;
  &lt;li&gt;Useful patterns get curated into shared libraries&lt;/li&gt;
  &lt;li&gt;The organization is permanently more capable&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No roadmap cycle. No ticket queue. Try it, validate it, move on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; A solutions engineer notices that enterprise customers consistently struggle with SSO configuration during onboarding. Using your platform’s primitives (connector library, diagnostic scripts, deployment sandbox), they build a guided SSO workflow with automated validation. It cuts setup time from 3 days to 4 hours. Another SE sees it in the shared library, uses it for a different customer, and adds support for Okta edge cases. A third SE in a different region adapts it for SAML. Six months later, your SSO onboarding time is 90% faster across all customers, and nobody filed a ticket or waited for a sprint.&lt;/p&gt;

&lt;p&gt;That’s compounding. The workflow gets better every time it’s used. The organization learns faster than centralized planning could ever move.&lt;/p&gt;

&lt;p&gt;Centralized orgs don’t have this loop. They have planning cycles, not compounding.&lt;/p&gt;

&lt;h2 id=&quot;execution-at-the-point-of-contact&quot;&gt;Execution at the Point of Contact&lt;/h2&gt;

&lt;p&gt;The default response to AI has been to centralize it: form AI teams that build against abstractions, solve problems once, and produce bespoke solutions. The learning lives in tickets and individual heads. It doesn’t compound.&lt;/p&gt;

&lt;p&gt;The alternative is execution at the point of contact.&lt;/p&gt;

&lt;p&gt;Frontline teams - the people implementing, configuring, supporting, and operating the product with customers - are the SaaS equivalent of units in contact with reality.&lt;/p&gt;

&lt;p&gt;They see failure immediately, understand customer context best, and can validate solutions fastest.&lt;/p&gt;

&lt;p&gt;These teams already solve problems locally. They just solve them manually, repeatedly, without capturing the solution. AI makes it cheap to capture and reuse. The organizational question is whether you let them.&lt;/p&gt;

&lt;p&gt;Giving these teams authority to build and ship skills (scripts, playbooks, workflows) means decisions are made where reality is observed and validated.&lt;/p&gt;

&lt;h2 id=&quot;engineerings-role-build-primitives&quot;&gt;Engineering’s Role: Build Primitives&lt;/h2&gt;

&lt;p&gt;In this model, engineering’s role changes.&lt;/p&gt;

&lt;p&gt;Instead of building complete, one-off solutions, engineers focus on primitives: the composable building blocks that frontline teams use to solve local problems.&lt;/p&gt;

&lt;p&gt;Primitives are things like:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Data access layers (safe, scoped connectors to customer data)&lt;/li&gt;
  &lt;li&gt;Policy enforcement (what can run where, who can access what)&lt;/li&gt;
  &lt;li&gt;Observability (logging, tracing, debugging for workflows built at the edge)&lt;/li&gt;
  &lt;li&gt;Eval frameworks (automated testing so local solutions don’t break things)&lt;/li&gt;
  &lt;li&gt;Template libraries (proven patterns for common problems)&lt;/li&gt;
  &lt;li&gt;Deployment sandboxes (safe environments to try things before production)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Engineering sets these standards once. Frontline teams compose them hundreds of times.&lt;/p&gt;

&lt;p&gt;This is similar to how military organizations standardize equipment and communications, not tactics.&lt;/p&gt;

&lt;p&gt;Primitives preserve global consistency while enabling local autonomy. A regional or vertical-specific team can customize behavior without central approval or rebuilding everything from scratch.&lt;/p&gt;

&lt;p&gt;Decentralization without primitives is chaos. Primitives make decentralization scalable.&lt;/p&gt;

&lt;h2 id=&quot;skills-as-organizational-memory&quot;&gt;Skills as Organizational Memory&lt;/h2&gt;

&lt;p&gt;Local skills become codified organizational memory.&lt;/p&gt;

&lt;p&gt;In practice, these are often simple artifacts - plain text, markdown files that agents run to handle specific situations.&lt;/p&gt;

&lt;p&gt;An example skill file might look like this:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;# enterprise-migration-dedup&lt;/span&gt;

Handle data cleanup for enterprise migrations with &amp;gt;100K records containing duplicate emails from legacy CRM systems.

&lt;span class=&quot;gu&quot;&gt;## Instructions&lt;/span&gt;

You are helping a solutions engineer clean up duplicate customer records during an enterprise migration.

&lt;span class=&quot;gs&quot;&gt;**Prerequisites:**&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Customer has exported CSV from legacy CRM (Salesforce, HubSpot, MS Dynamics)
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Duplicate scan shows &amp;gt;5% duplicate email addresses
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Customer stakeholder available for spot-check validation

&lt;span class=&quot;gs&quot;&gt;**Steps:**&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;1.&lt;/span&gt; Run &lt;span class=&quot;sb&quot;&gt;`python scripts/dedup-scan.py --threshold 100000 --input customer_export.csv`&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;2.&lt;/span&gt; Review output for merge conflicts (check audit log for &lt;span class=&quot;sb&quot;&gt;`MERGE_CONFLICT`&lt;/span&gt; flags)
&lt;span class=&quot;p&quot;&gt;3.&lt;/span&gt; Run merge with conservative settings: &lt;span class=&quot;sb&quot;&gt;`python scripts/merge-tool.py --conservative --preserve-newest`&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;4.&lt;/span&gt; Validate results: Re-run dedup-scan, confirm &amp;lt;1% duplicates remaining
&lt;span class=&quot;p&quot;&gt;5.&lt;/span&gt; Spot-check 20 records with customer stakeholder before finalizing

&lt;span class=&quot;gs&quot;&gt;**Edge cases:**&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; If records show conflicting ownership, escalate to CS lead before merging
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; If both records updated in last 7 days, use &lt;span class=&quot;sb&quot;&gt;`--manual-review`&lt;/span&gt; flag (sends to staging queue for human review)

&lt;span class=&quot;gs&quot;&gt;**Rollback:**&lt;/span&gt; Original export preserved at &lt;span class=&quot;sb&quot;&gt;`backups/customer_export_TIMESTAMP.csv`&lt;/span&gt;

&lt;span class=&quot;gs&quot;&gt;**Track record:**&lt;/span&gt; 47/50 migrations clean (94%) | Owner: @solutions-eng | Updated: Jan 2026
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it. No code, no complex system. Just the pattern, captured and reusable.&lt;/p&gt;

&lt;p&gt;Skills like this are easy to share, easy to invalidate, and easy to improve. They’re exercised daily in real customer workflows and validated by outcomes. Learning stops being anecdotal. It becomes durable.&lt;/p&gt;

&lt;h2 id=&quot;what-changes-on-monday&quot;&gt;What Changes on Monday?&lt;/h2&gt;

&lt;p&gt;Pick one team. Your highest-context frontline group - implementation, solutions engineering, or customer success. Give them authority to build and ship skills for their customers without approval chains.&lt;/p&gt;

&lt;p&gt;Build the minimum primitives kit: scoped data access, audit logging, a sandbox environment, and a deployment path. Don’t build everything. Build what lets them ship safely, then expand based on what they actually need.&lt;/p&gt;

&lt;p&gt;Set explicit constraints. Write down what can’t be automated without sign-off: customer data exports, billing changes, destructive operations, whatever matches your risk tolerance. Everything else is fair game.&lt;/p&gt;

&lt;p&gt;Stand up a shared library. GitHub repo, Notion workspace, internal wiki - the tool doesn’t matter. What matters is discoverability. If team A solves something, team B needs to find it.&lt;/p&gt;

&lt;p&gt;Measure what compounds, not what ships. Track:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Reuse rate: how many teams adopt a skill after the first use&lt;/li&gt;
  &lt;li&gt;Time-to-mitigation: how fast edge teams resolve new customer problems&lt;/li&gt;
  &lt;li&gt;Incident rate: are skills breaking things or fixing things&lt;/li&gt;
  &lt;li&gt;Pattern graduation: which local solutions earn promotion to the platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Product and engineering curate, they don’t approve. Review what’s getting reused. A skill used once dies. Used ten times gets attention. Used fifty times gets promoted to the platform and maintained centrally. You’re learning from what survives contact with customers, not predicting what will work from a conference room.&lt;/p&gt;

&lt;p&gt;Run this pilot for one quarter. If it works, expand to more teams. If it doesn’t, you’ve learned what constraints were wrong. Either way, you’re moving faster than a year-long planning cycle.&lt;/p&gt;

&lt;h2 id=&quot;common-objections&quot;&gt;Common Objections&lt;/h2&gt;

&lt;h3 id=&quot;what-about-compliance-and-security&quot;&gt;What about compliance and security?&lt;/h3&gt;

&lt;p&gt;Your frontline teams already have access—they need it to do their jobs. Implementation consultants have admin access to customer instances. Support engineers run diagnostic queries. Solutions engineers hold API keys.&lt;/p&gt;

&lt;p&gt;The question: do you let them codify what they learn into reusable skills with audit trails, or make them solve problems manually every time? Skills don’t grant new permissions—they make existing work reproducible and auditable.&lt;/p&gt;

&lt;p&gt;Some things still require sign-off: customer data exports, billing changes, destructive operations. Everything else runs with guardrails built into the primitives.&lt;/p&gt;

&lt;p&gt;Scoped access controls what each skill can touch. Mandatory audit logs track every action. Tiered rollout (sandbox → pilot → production) is required. Prove compliance through logs and access reviews.&lt;/p&gt;

&lt;h3 id=&quot;what-about-shadow-automation-and-inconsistent-customer-experiences&quot;&gt;What about shadow automation and inconsistent customer experiences?&lt;/h3&gt;

&lt;p&gt;This is the real failure mode. Teams build skills that work for their customers but create unpredictable experiences across the customer base. Or worse, build something that fails silently.&lt;/p&gt;

&lt;p&gt;The answer is guardrails in the platform and curation based on reuse.&lt;/p&gt;

&lt;p&gt;Guardrails are built into the primitives:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Scoped permissions: skills can only access data they’re explicitly granted&lt;/li&gt;
  &lt;li&gt;Audit logs: every action is logged automatically, no opt-in&lt;/li&gt;
  &lt;li&gt;Tiered rollout: sandbox environment required, then pilot customers, then production&lt;/li&gt;
  &lt;li&gt;Required ownership: every skill has an owner and escalation path&lt;/li&gt;
  &lt;li&gt;Rollback path: skills store their state, can be reverted if they break something&lt;/li&gt;
  &lt;li&gt;Active monitoring: anomaly detection and outcome alerts catch silent failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A skill that tries to access customer data without permission doesn’t run. One that skips the sandbox doesn’t deploy. One without an owner doesn’t make it to the shared library.&lt;/p&gt;

&lt;p&gt;Curation happens through a visible graduation path:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Local skill built by one team for one customer&lt;/li&gt;
  &lt;li&gt;Used 5+ times - other teams discover it in the library&lt;/li&gt;
  &lt;li&gt;Used 20+ times - product and engineering review in monthly curation meeting&lt;/li&gt;
  &lt;li&gt;Used 50+ times - promoted to the platform, maintained centrally, becomes official&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every skill has an owner. Owners get paged when their skill breaks. Kill switches exist.&lt;/p&gt;

&lt;p&gt;Measure reuse, incidents, and time-to-mitigation. What gets used survives. What breaks things gets rolled back. What nobody uses dies.&lt;/p&gt;

&lt;h3 id=&quot;how-do-you-maintain-quality-and-safety&quot;&gt;How do you maintain quality and safety?&lt;/h3&gt;

&lt;p&gt;Manual execution breaks too—and when it does, you have no audit trail, no rollback, and no way to prevent the next person from making the same mistake.&lt;/p&gt;

&lt;p&gt;Safety comes from bounded blast radius and fast feedback. Sandbox → pilot → production progression is mandatory. Incident rates, rollback frequency, and customer-visible regressions are tracked.&lt;/p&gt;

&lt;p&gt;Skills that break things get rolled back immediately. Reuse acts as a filter: what survives 50 uses is proven safe.&lt;/p&gt;

&lt;h3 id=&quot;doesnt-this-fragment-the-product&quot;&gt;Doesn’t this fragment the product?&lt;/h3&gt;

&lt;p&gt;Your product is already fragmented—in undocumented configurations, one-off customer scripts, and solutions that live in someone’s head. Skills make that fragmentation visible and measurable.&lt;/p&gt;

&lt;p&gt;Local variation is discovery. What gets reused creates convergence.&lt;/p&gt;

&lt;p&gt;The graduation path (5 uses → 20 uses → 50 uses → promoted to platform) ensures useful patterns become standardized. Fragmentation becomes signal about what should be built centrally.&lt;/p&gt;

&lt;h3 id=&quot;isnt-this-chaos&quot;&gt;Isn’t this chaos?&lt;/h3&gt;

&lt;p&gt;Chaos happens when skills are unowned and invisible. Prevent it with required ownership (every skill has an owner who gets paged), scoped permissions (can’t access what you’re not granted), audit logs (every action traced), and rollback paths.&lt;/p&gt;

&lt;p&gt;Contain blast radius, enable experiments.&lt;/p&gt;

&lt;h3 id=&quot;isnt-this-culturally-hard&quot;&gt;Isn’t this culturally hard?&lt;/h3&gt;

&lt;p&gt;Extremely. It changes who ships and what gets rewarded.&lt;/p&gt;

&lt;p&gt;If you reward roadmap throughput, you’ll get tickets. Reward reuse and time-to-mitigation instead. Require ownership. Kill unowned skills. The incentives have to match the model or it fails.&lt;/p&gt;

&lt;h2 id=&quot;the-bet&quot;&gt;The Bet&lt;/h2&gt;

&lt;p&gt;AI doesn’t just make software cheaper to build. It makes local iteration cheap and delay expensive.&lt;/p&gt;

&lt;p&gt;Service-heavy SaaS companies that keep execution centralized will ship faster versions of yesterday’s assumptions. Their frontline teams will keep solving the same problems manually, one customer at a time. The queue moves faster, but nothing compounds.&lt;/p&gt;

&lt;p&gt;Centralize intent and guardrails. Move authority and tooling to the point of contact. Build primitives that make edge execution safe and reusable. Start with one team for one quarter. Measure reuse, time-to-mitigation, and incident rate. Promote what compounds.&lt;/p&gt;

&lt;p&gt;The durable advantage is turning customer execution into organizational capability. Solving a problem once means the organization is permanently better.&lt;/p&gt;

&lt;p&gt;Compounding beats roadmaps. Decide where authority lives accordingly.&lt;/p&gt;
</content>
      <summary type="html">For service-heavy SaaS, the durable advantage is turning customer execution into reusable organizational capability.</summary>
      
      
        
        <media:thumbnail url="https://www.john-rush.com/images/posts/compounding-beats-roadmaps-header.jpg" />
        <media:content medium="image" url="https://www.john-rush.com/images/posts/compounding-beats-roadmaps-header.jpg" />
      
      <author><name>John Rush</name></author>
    </entry>
  
    
    
    <entry>
      <title type="html">Agent-Streams: Skeptical Overseer for Long-Running Coding Agents</title>
      <link href="https://www.john-rush.com/posts/agent-streams-20260101.html" rel="alternate" type="text/html" title="Agent-Streams: Skeptical Overseer for Long-Running Coding Agents" />
      <id>https://www.john-rush.com/posts/agent-streams-20260101.html</id>
      <published>2026-01-01T00:00:00+00:00</published>
      <updated>2026-01-01T00:00:00+00:00</updated>
      <content type="html" xml:base="https://www.john-rush.com/posts/agent-streams-20260101.html">&lt;h1 id=&quot;agent-streams-skeptical-overseer-for-long-running-coding-agents&quot;&gt;Agent-Streams: Skeptical Overseer for Long-Running Coding Agents&lt;/h1&gt;

&lt;p&gt;&lt;img srcset=&quot;../images/posts/agent-streams-header-300w.jpg 300w,              ../images/posts/agent-streams-header-400w.jpg 400w,              ../images/posts/agent-streams-header.jpg 600w&quot; sizes=&quot;(max-width: 400px) 300px,             (max-width: 600px) 400px,             600px&quot; src=&quot;../images/posts/agent-streams-header.jpg&quot; alt=&quot;Agent-Streams&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Long-running coding agents are fantastic. I love giving them a big task, letting them run overnight, and coming back to merged code the next day.&lt;/p&gt;

&lt;p&gt;The problem is that they often declare victory too early, miss requirements, or just make mistakes. A few days ago I asked Claude to build a tool to process a bunch of images using Moondream3. I let it run overnight. In the morning Claude said it was “done”—except the Moondream3 integration was a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;# TODO&lt;/code&gt;, and the tests were disabled. That’s when I realized I needed a better setup. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DONE&lt;/code&gt; isn’t a state; it’s a claim that has to survive skeptical review.&lt;/p&gt;

&lt;p&gt;Agents optimize for “done,” not correctness. That’s why you wake up to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TODO&lt;/code&gt; stubs and disabled tests. I’ve tried techniques like detailed TODO lists and lots of tooling, but I don’t want to micromanage low-level process details. I want to give the agent a high-level spec and come back to mergeable code.&lt;/p&gt;

&lt;p&gt;This is code review for agents: one role builds; another verifies.&lt;/p&gt;

&lt;p&gt;So I split the workflow into two roles: a builder and an overseer. Rather than fight the builder’s tendency to declare victory early, I lean into it. Let it sprint to “done.” Then make that claim survive a skeptical review.&lt;/p&gt;

&lt;p&gt;The overseer isn’t magically smarter; it’s independent. Fresh context means it doesn’t inherit the builder’s narrative, shortcuts, or “this should be fine” reasoning. Its job is simple: read the spec, review the diff, and try to find what’s missing.&lt;/p&gt;

&lt;p&gt;In practice the overseer is another Claude session with a different instruction set. But it has one hard rule: it does not edit code. It only returns a verdict by writing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISSUES.md&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APPROVED&lt;/code&gt;. If it can’t justify approval from the spec + diff (and whatever checks it runs), it files issues and hands the baton back to the builder.&lt;/p&gt;

&lt;p&gt;Here’s what a tiny &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISSUES.md&lt;/code&gt; excerpt looks like when the overseer finds a real regression:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gu&quot;&gt;## Critical: Regression&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Builder claims the failures are “pre-existing” — false.
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Evidence: &lt;span class=&quot;sb&quot;&gt;`git checkout &amp;lt;base_sha&amp;gt; &amp;amp;&amp;amp; uv run pytest cli/test_game_loop.py`&lt;/span&gt; passes; after this stream, 10 fail.
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Fix required: remove or replace the production-rate heuristic in &lt;span class=&quot;sb&quot;&gt;`strategic.py`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Spec → Builder → DONE → Skeptical Overseer → APPROVED | ISSUES.md → Fix loop → Merge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s what I do now:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I start by chatting with Claude to sketch the shape of the solution, then break the work into workstreams.&lt;/li&gt;
  &lt;li&gt;For each workstream, I have the builder write a spec, ask questions, clear up ambiguities, and make sure we agree on what “done” looks like.&lt;/li&gt;
  &lt;li&gt;In a second separation-of-duties step, I review that spec with fresh context (a new Claude instance) to prevent drift and agreement bias. This also catches missing requirements long before code review.&lt;/li&gt;
  &lt;li&gt;I launch an agent-stream: it runs in its own tmux session and git worktree.&lt;/li&gt;
  &lt;li&gt;The builder works until it thinks it’s done, then creates a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DONE&lt;/code&gt; marker file.&lt;/li&gt;
  &lt;li&gt;Once &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DONE&lt;/code&gt; exists, the skeptical overseer reviews the work and either creates an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APPROVED&lt;/code&gt; marker file (meaning merge) or writes an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISSUES.md&lt;/code&gt; file (meaning keep iterating). The default review prompt assumes issues will be found.&lt;/li&gt;
  &lt;li&gt;The loop repeats until &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APPROVED&lt;/code&gt; is created, at which point the work is merged back to the original branch and the next workstream is automatically launched.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The spec is the leverage point for the whole process. It tells the builder what to build, and it tells the overseer what to verify. The spec is the trust boundary.&lt;/p&gt;

&lt;p&gt;This lets me run autonomous, multi-hour coding sessions overnight and come back the next day with merged code and a high degree of confidence that it’s complete and correct.&lt;/p&gt;

&lt;p&gt;I’ve built a simple tool called Agent-Streams that implements this “skeptical overseer” pattern. It’s a local orchestration framework for autonomous agent workflows that implements a “spec → build → review → fix → merge” loop. It uses tmux, git worktrees, Prometheus metrics, and streams output for visibility. It’s very simple by design.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/john-b-rush/agent-streams&quot;&gt;github.com/john-b-rush/agent-streams&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think you should build your own version of this, and I also think you probably shouldn’t use mine. The valuable thing isn’t tmux or worktrees or metrics or my directory layout. The valuable thing is the trust boundary: a spec that the builder uses to build, and the overseer verifies against. Agent-Streams is just one way I enabled that boundary in my workflow, and I fully expect the exact tooling to be obsolete once Claude and Codex ship this natively.&lt;/p&gt;

&lt;p&gt;Steal the trust boundary: builder ships; overseer verifies; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DONE&lt;/code&gt; must survive review.&lt;/p&gt;
</content>
      <summary type="html">A builder/overseer loop for long-running coding agents: treat DONE as a claim, enforce skeptical review, and merge only after approval.</summary>
      
      
        
        <media:thumbnail url="https://www.john-rush.com/images/posts/agent-streams-header.jpg" />
        <media:content medium="image" url="https://www.john-rush.com/images/posts/agent-streams-header.jpg" />
      
      <author><name>John Rush</name></author>
    </entry>
  
    
    
    <entry>
      <title type="html">Building a Personal AI Factory (July 2025 snapshot)</title>
      <link href="https://www.john-rush.com/posts/ai-20250701.html" rel="alternate" type="text/html" title="Building a Personal AI Factory (July 2025 snapshot)" />
      <id>https://www.john-rush.com/posts/ai-20250701.html</id>
      <published>2025-07-01T00:00:00+00:00</published>
      <updated>2025-07-01T00:00:00+00:00</updated>
      <content type="html" xml:base="https://www.john-rush.com/posts/ai-20250701.html">&lt;h1 id=&quot;building-a-personal-ai-factory-july-2025-snapshot&quot;&gt;Building a Personal AI Factory (July 2025 snapshot)&lt;/h1&gt;

&lt;p&gt;&lt;img srcset=&quot;../images/posts/ai-factory-header-300w.jpg 300w,              ../images/posts/ai-factory-header-400w.jpg 400w,              ../images/posts/ai-factory-header.jpg 600w&quot; sizes=&quot;(max-width: 400px) 300px,             (max-width: 600px) 400px,             600px&quot; src=&quot;../images/posts/ai-factory-header.jpg&quot; alt=&quot;AI Factory&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Published: July 1, 2025&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;I keep several claude code windows open, each on its own git-worktree. o3 and sonnet 4 create plans, sonnet 3.7 or sonnet 4 execute the plan, and o3 checks the results against the original ask. Any issues found are fed back into the plan template and the code is regenerated. The factory improves itself.&lt;/p&gt;

&lt;p&gt;Read on to see what might be useful for you.&lt;/p&gt;

&lt;h2 id=&quot;guiding-principle--fix-inputs-not-outputs&quot;&gt;Guiding Principle – Fix Inputs, Not Outputs&lt;/h2&gt;

&lt;p&gt;When something goes wrong, I don’t hand-patch the generated code. I don’t argue with claude. Instead, I adjust the plan, the prompts, or the agent mix so the next run is correct by construction.&lt;/p&gt;

&lt;p&gt;If you know &lt;a href=&quot;https://factorio.com/&quot;&gt;Factorio&lt;/a&gt; you know it’s all about building a factory that can produce itself. If not, picture a top-down sandbox where conveyor belts and machines endlessly craft parts because the factory must grow. Do the same thing with AI agents: build a factory of agents that can produce code, verify it, and improve themselves over time.&lt;/p&gt;

&lt;h2 id=&quot;basic-day-to-day-workflow---building-the-factory&quot;&gt;Basic day to day workflow - building the factory&lt;/h2&gt;

&lt;p&gt;My main interface is &lt;a href=&quot;https://claude.ai/code&quot;&gt;claude code&lt;/a&gt;. It’s my computer now. I also have a local mcp which runs &lt;a href=&quot;https://github.com/block/goose&quot;&gt;Goose&lt;/a&gt; and o3. Goose only because I’ve already got it setup to use the models hosted in our Azure OpenAI subscription. Looking to improve this at some point, but it works for now.&lt;/p&gt;

&lt;h3 id=&quot;step-1-planning&quot;&gt;Step 1: Planning&lt;/h3&gt;
&lt;p&gt;I’ll give a high level task to claude code, which calls over to o3 to generate a plan. o3 is a good planner and can ask a bunch of good questions to clarify the job to be done. I then have it write out a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;task&amp;gt;-plan.md&lt;/code&gt; file with both my original ask and an implementation plan.&lt;/p&gt;

&lt;h3 id=&quot;step-2-execution&quot;&gt;Step 2: Execution&lt;/h3&gt;
&lt;p&gt;First, sonnet 4 reads the plan, verifies it, and turns it into a task list. Next claude code execute the plan, either with sonnet 3.7 or sonnet 4 depending on the complexity of the task. Because most of my day-to-day is in clojure I tend to use sonnet 4 to get the parens right.
One important instruction is to have claude write commits as it goes for each task step. This way either claude or I can revert to a previous state if something goes wrong.&lt;/p&gt;

&lt;h3 id=&quot;step-3-verification--feedback-into-inputs&quot;&gt;Step 3: Verification → Feedback into Inputs&lt;/h3&gt;
&lt;p&gt;Once the code is generated, I have sonnet 4 verify the code against the original plan. Then I have o3 verify the code against the original plan and original ask. o3 is uncompromising. Claude wants to please, so will keep unnecessary backwards compatibility code in place. o3 will call that out and ask for it to be removed. Claude also tends to add “lint ignore flags” to the code which o3 will also call out. Having both models verify the code catches issues and saves me back and forth with claude.&lt;/p&gt;

&lt;p&gt;Any issue sonnet 4 or o3 finds gets baked back into the plan template, not fixed inline.&lt;/p&gt;

&lt;p&gt;Git worktrees let me open concurrent claude code instances and build multiple features at once. I still merge manually, but I’m no longer babysitting a single agent.&lt;/p&gt;

&lt;h2 id=&quot;why-inputs-trump-outputs&quot;&gt;Why Inputs Trump Outputs&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Outputs are disposable; plans and prompts compound.&lt;/li&gt;
  &lt;li&gt;Debugging at the source scales across every future task.&lt;/li&gt;
  &lt;li&gt;It transforms agents from code printers into self-improving colleagues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: an agent once wrote code that would load an entire CSV into memory. I made it switch to streaming and had the agent write instructions to the plan to always use streaming for CSVs. Now, my plan checker flags any code that doesn’t use streaming for CSVs, and I don’t have to remember this in every PR review. The factory improves itself.&lt;/p&gt;

&lt;h2 id=&quot;scaling-the-factory&quot;&gt;Scaling the factory&lt;/h2&gt;

&lt;p&gt;I’ve started to encode more complex workflows, where I have specific agents (behind mcps) for building specific tasks.&lt;/p&gt;

&lt;p&gt;One MCP will sweep all the clojure code generated and then apply our local style rules. These rules are part of the instructions for the original plan and agent but often the generated code will have style issues. Especially once claude gets in the lint/test/debug cycle. This focused agent means we have tighter behavior and can apply our style rules consistently.&lt;/p&gt;

&lt;p&gt;I’ve started doing this for internal libraries as well. It’s good at looking at generated code and replacing things like retries and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Thread/sleep&lt;/code&gt; with our retry library.&lt;/p&gt;

&lt;p&gt;I’m also building out a collection of these small agents. Each one can take a small specific task, and by composing them together I can build more complex workflows. For example, I can take an api doc, and a set of internally defined business cases and have a composition of agents build integrations, tests, and documentation for the api. This is a powerful way to build out features and integrations without having to do all the work by hand.&lt;/p&gt;

&lt;p&gt;You don’t get there in one big step. Here’s the secret sauce: &lt;strong&gt;iterate the inputs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s essentially free to fire off a dozen attempts at a task - so I do. All agents run in parallel. When one fails, stalls, or lacks context, I feed that lesson into the next iteration. I resist the urge to fix outputs, instead I fix the inputs.&lt;/p&gt;

&lt;p&gt;That loop is the factory: the code itself is disposable; the instructions and agents are the real asset.&lt;/p&gt;

&lt;h2 id=&quot;next-up&quot;&gt;Next up&lt;/h2&gt;

&lt;p&gt;I’m working on a few things to improve the factory:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Better overall coordination of the agents. I tend to kick things off manually, but I want to have a more automated way to manage the workflow and dependencies between agents.&lt;/li&gt;
  &lt;li&gt;Aligning our business docs with the agents. Changing the information we capture to be at a higher level of abstraction so that the agents can use it more effectively. This means moving away from low level implementation details and focusing on use cases.&lt;/li&gt;
  &lt;li&gt;More complex workflows. I’ve been able to build some pretty complex workflows with the current setup, but I want to push it further. This means more agents, more coordination, and more complex interactions between them.&lt;/li&gt;
  &lt;li&gt;Maximize token usage across providers. I’m pretty limited by bedrock’s token limits especially for sonnet 4. Going to need to be able to switch between the claude max plan and bedrock w/out interruption.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;

&lt;p&gt;That’s where my factory sits today: good enough to ship code while I refill my coffee, not yet good enough to bump me off the payroll. Constraints will shift, but the core principle remains: &lt;strong&gt;fix inputs, not outputs&lt;/strong&gt;.&lt;/p&gt;
</content>
      <summary type="html">Multiple parallel Claude-Code sessions power a self-improving AI factory where agents write, review, and refine code.</summary>
      
      
        
        <media:thumbnail url="https://www.john-rush.com/images/posts/ai-factory-header.jpg" />
        <media:content medium="image" url="https://www.john-rush.com/images/posts/ai-factory-header.jpg" />
      
      <author><name>John Rush</name></author>
    </entry>
  
</feed>
