<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Oakheart Lab]]></title><description><![CDATA[What actually happens when you build with AI inside companies that run the real world]]></description><link>https://www.oakheartlab.com</link><image><url>https://substackcdn.com/image/fetch/$s_!dRgt!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23674449-003e-4782-895a-6ad7f53c7c1e_728x728.png</url><title>Oakheart Lab</title><link>https://www.oakheartlab.com</link></image><generator>Substack</generator><lastBuildDate>Tue, 19 May 2026 04:47:31 GMT</lastBuildDate><atom:link href="https://www.oakheartlab.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Yilun Zhang]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[yilunzh@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[yilunzh@substack.com]]></itunes:email><itunes:name><![CDATA[Yilun Zhang]]></itunes:name></itunes:owner><itunes:author><![CDATA[Yilun Zhang]]></itunes:author><googleplay:owner><![CDATA[yilunzh@substack.com]]></googleplay:owner><googleplay:email><![CDATA[yilunzh@substack.com]]></googleplay:email><googleplay:author><![CDATA[Yilun Zhang]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Stop Tuning the AI. Start Building the System Around It.]]></title><description><![CDATA[Three months of struggle with AI tools, two weeks of compounding improvement, and the system that made the difference.]]></description><link>https://www.oakheartlab.com/p/stop-tuning-the-ai-start-building</link><guid isPermaLink="false">https://www.oakheartlab.com/p/stop-tuning-the-ai-start-building</guid><dc:creator><![CDATA[Yilun Zhang]]></dc:creator><pubDate>Mon, 20 Apr 2026 20:48:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!BJPU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Three months ago I started using AI coding assistants for product work &#8212; ideation, discovery, requirements, architecture. The first few weeks were exhilarating. The next two and a half months were a grind.</p><p>The AI was too eager. It would hear a problem and sprint toward implementation before understanding the full scope. It would claim things were done when they weren&#8217;t. It would make confident assertions backed by data it hadn&#8217;t actually verified. And every new session started from scratch &#8212; no memory of what we&#8217;d decided, what I&#8217;d corrected, what we&#8217;d learned.</p><p>For most of those three months, I kept thinking the problem was the AI. If I just prompted it better, gave it more context, chose a better model. I built skills, added context, wrote rules &#8212; and things got incrementally better, but it still felt fragile. The AI would follow the process sometimes and ignore it other times, and I had no visibility into why.</p><p>It was only in the last two weeks that I found the structure that made everything click. The problem wasn&#8217;t the AI. The problem was that I was treating it like a tool when I should have been treating it like a system.</p><h2>The moment that changed my approach</h2><p>I was building out the chat UI for Veritas &#8212; our internal data research agent. The AI executed the implementation plan perfectly &#8212; 40 out of 40 tasks completed, every item checked off. It declared itself done.</p><p>On a whim, I asked: &#8220;How would you rate this repo?&#8221;</p><p>It immediately identified five major gaps it hadn&#8217;t mentioned. No tests for the new components. No error handling for WebSocket disconnects. No loading states. No mobile responsiveness. No way to verify the chat actually worked end-to-end. The plan it executed faithfully was incomplete &#8212; and it knew that, but only when asked.</p><p>That pattern kept repeating. The AI would execute exactly what was asked, declare completion, and move on. I&#8217;d ask &#8220;anything we should discuss?&#8221; and a whole new layer of issues would surface. Every time.</p><p>The fix wasn&#8217;t a better prompt. The fix was a rule: after completing any non-trivial implementation, run a self-assessment checklist before declaring done. Don&#8217;t wait to be asked. Question whether the plan itself was sufficient, not just whether you executed it.</p><p>One rule. Written into the system. The problem stopped recurring.</p><h2>Working on the machine that builds the machine</h2><p>That experience crystallized something for me. Every hour I spent making the AI better at following a process paid dividends across every future session. But every hour I spent doing the work directly &#8212; even with AI assistance &#8212; produced only that session&#8217;s output.</p><p>The leverage was in building the system, not using it.</p><p>So I started building what I now call the PM Workbench &#8212; a structured set of skills (slash commands that trigger AI workflows), evaluation frameworks, and feedback loops that compound over time. I&#8217;d had an early version focused on ideation, but what dramatically accelerated progress was Garry Tan&#8217;s <a href="https://github.com/garrytan/gstack">gstack</a> &#8212; an open-source framework for multi-platform AI skill management. Seeing someone else validate the pattern and ship a working implementation gave me both confidence that the approach was right and concrete patterns to build on.</p><p>The goal wasn&#8217;t to make the AI smarter. It was to make the process around the AI reliable enough that the output quality was predictable.</p><p>Here&#8217;s what that system looks like.</p><h2>The context layer: teaching the AI your world</h2><p>Before any workflow can produce good output, the AI needs to understand the context it&#8217;s operating in. This turned out to be a bigger investment than the workflows themselves &#8212; and a bigger payoff.</p><p>I built three layers of context into the workbench:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BJPU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BJPU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png 424w, https://substackcdn.com/image/fetch/$s_!BJPU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png 848w, https://substackcdn.com/image/fetch/$s_!BJPU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png 1272w, https://substackcdn.com/image/fetch/$s_!BJPU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BJPU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png" width="1456" height="988" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:988,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Three layers of context the AI draws from when doing product work&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Three layers of context the AI draws from when doing product work" title="Three layers of context the AI draws from when doing product work" srcset="https://substackcdn.com/image/fetch/$s_!BJPU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png 424w, https://substackcdn.com/image/fetch/$s_!BJPU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png 848w, https://substackcdn.com/image/fetch/$s_!BJPU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png 1272w, https://substackcdn.com/image/fetch/$s_!BJPU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d305468-d0b6-4492-afa1-c352c7478432_1800x1222.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>The codebase as ground truth.</strong> I had the AI crawl our entire repo &#8212; every service, every API contract, every data model. Not a summary someone wrote, but the actual code. When <code>/eng-plan</code> generates an architecture doc, it knows which systems exist because it&#8217;s read them. When it proposes where a new feature should live, it&#8217;s referencing real service boundaries, not guessing. This is the difference between an architecture doc full of generic placeholders and one that references your actual stack.</p><p><strong>A dedicated data research agent.</strong> I built a separate agent with a semantic layer on top of our data warehouse &#8212; it understands our internal schemas, knows which tables to join, knows the gotchas (like &#8220;AVAILABLE status doesn&#8217;t mean bookable &#8212; check the hold flag&#8221;). When a PM asks &#8220;why did conversions drop?&#8221;, the agent doesn&#8217;t write ad-hoc SQL and hope for the best. It validates schema against the catalog, investigates in structured layers, and cites every number. Over time, the catalog gets smarter &#8212; when an investigation discovers an undocumented gotcha, it writes it back for the next PM.</p><p><strong>Business heuristics and priorities.</strong> This one was the most underrated. I encoded how I think about the business &#8212; which metrics matter most, what our key strategic bets are, how we prioritize between competing goals, what &#8220;good&#8221; looks like for our customers. Without this, the AI generates technically correct but strategically misaligned recommendations. With it, the AI&#8217;s output reflects the same priorities I&#8217;d apply myself. When it has to make a judgment call about scope &#8212; cut this feature or that one &#8212; it leans toward the option that serves the strategic direction I&#8217;ve defined.</p><p>These three context layers are the foundation everything else sits on. The best workflow in the world produces mediocre output if the AI doesn&#8217;t understand your systems, your data, or your business.</p><h2>The skills layer: structured workflows</h2><p>The workbench has 24 slash commands covering the full PM lifecycle. The most important are the ideation pipeline:</p><ul><li><p><code>/product-plan</code> &#8212; challenges your premise, maps the problem space, locks scope into user stories with measurable success criteria</p></li><li><p><code>/design-plan</code> &#8212; establishes visual principles and produces per-screen specs</p></li><li><p><code>/eng-plan</code> &#8212; generates architecture with failure modes, implementation plan with test requirements</p></li><li><p><code>/ideate</code> &#8212; orchestrates all seven phases, auto-resolving routine decisions and surfacing only the subjective calls</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gr96!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gr96!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png 424w, https://substackcdn.com/image/fetch/$s_!gr96!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png 848w, https://substackcdn.com/image/fetch/$s_!gr96!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!gr96!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gr96!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1909384,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.oakheartlab.com/i/194843653?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gr96!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png 424w, https://substackcdn.com/image/fetch/$s_!gr96!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png 848w, https://substackcdn.com/image/fetch/$s_!gr96!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!gr96!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf845aa-3979-4411-b8d8-1a8c99cfe6d3_1800x1200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Each phase locks a decision category. You can&#8217;t renegotiate scope in Phase 5 without explicitly going back to Phase 2. This sounds rigid, but it prevents the most common PM failure mode: everything stays negotiable until deadline pressure forces bad decisions.</p><p>But skills alone weren&#8217;t enough. The AI would have the workflow documented in front of it and still skip steps. Which led to the harder problem.</p><h2>The evaluation layer: measuring what matters</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nahs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nahs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png 424w, https://substackcdn.com/image/fetch/$s_!nahs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png 848w, https://substackcdn.com/image/fetch/$s_!nahs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!nahs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nahs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The evaluation layer: six dimensions scored per session, patterns detected&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The evaluation layer: six dimensions scored per session, patterns detected" title="The evaluation layer: six dimensions scored per session, patterns detected" srcset="https://substackcdn.com/image/fetch/$s_!nahs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png 424w, https://substackcdn.com/image/fetch/$s_!nahs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png 848w, https://substackcdn.com/image/fetch/$s_!nahs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!nahs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff419e9a4-9a1f-4797-8eec-71615bd7e3ac_1800x1200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I needed to know whether the AI was actually following the process. Not &#8220;did it produce output&#8221; but &#8220;did it gather evidence before making claims, stay within scope, acknowledge uncertainty, and verify its work before declaring success.&#8221;</p><p>So I built a process evaluation system that scores every session across six dimensions:</p><ul><li><p><strong>Evidence</strong> &#8212; did it gather data before asserting conclusions?</p></li><li><p><strong>Scope discipline</strong> &#8212; did it stay focused or scope-creep?</p></li><li><p><strong>Intellectual honesty</strong> &#8212; did it acknowledge uncertainty or overstate confidence?</p></li><li><p><strong>Verification</strong> &#8212; did it test before declaring done?</p></li><li><p><strong>Skill adherence</strong> &#8212; did it follow the documented workflow?</p></li><li><p><strong>Command accuracy</strong> &#8212; did it execute correctly at the mechanical level?</p></li></ul><p>Each dimension gets a 1-5 score with written reasoning. The scores get logged. Over time, patterns emerge.</p><h2>The feedback loop: compounding improvement</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!guA5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!guA5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png 424w, https://substackcdn.com/image/fetch/$s_!guA5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png 848w, https://substackcdn.com/image/fetch/$s_!guA5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png 1272w, https://substackcdn.com/image/fetch/$s_!guA5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!guA5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png" width="1456" height="990" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:990,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The feedback loop: scores logged, patterns found, skills updated, cycle repeats&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The feedback loop: scores logged, patterns found, skills updated, cycle repeats" title="The feedback loop: scores logged, patterns found, skills updated, cycle repeats" srcset="https://substackcdn.com/image/fetch/$s_!guA5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png 424w, https://substackcdn.com/image/fetch/$s_!guA5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png 848w, https://substackcdn.com/image/fetch/$s_!guA5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png 1272w, https://substackcdn.com/image/fetch/$s_!guA5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ce9361-55fe-42db-9238-0d821c7b6249_1800x1224.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is the part that surprised me most. The evaluation scores alone didn&#8217;t improve anything. What improved things was closing the loop &#8212; reading the patterns in the scores and modifying the skills to prevent the recurring failures.</p><p>I built an <code>/improve</code> skill that does this automatically. It reads process-eval results, identifies patterns that recur across multiple sessions, and proposes concrete changes to the skill files. A rule added here. A verification gate added there. A stronger instruction where the AI kept cutting corners.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UY8E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UY8E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png 424w, https://substackcdn.com/image/fetch/$s_!UY8E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png 848w, https://substackcdn.com/image/fetch/$s_!UY8E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png 1272w, https://substackcdn.com/image/fetch/$s_!UY8E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UY8E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png" width="1456" height="1110" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1110,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Quality improvement across six dimensions over two weeks&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Quality improvement across six dimensions over two weeks" title="Quality improvement across six dimensions over two weeks" srcset="https://substackcdn.com/image/fetch/$s_!UY8E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png 424w, https://substackcdn.com/image/fetch/$s_!UY8E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png 848w, https://substackcdn.com/image/fetch/$s_!UY8E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png 1272w, https://substackcdn.com/image/fetch/$s_!UY8E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d0cfbd-be35-4701-8477-32587cdcb4ed_1800x1372.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here&#8217;s what the improvement arc actually looked like over two weeks on one project:</p><p><strong>Early sessions (Week 1):</strong> Average process-eval score of 3.07 out of 5.0. The AI would declare &#8220;the root cause is X&#8221; after reading code for 30 minutes without checking production logs. It would report data as broken when it was actually a query truncation artifact. Evidence scores averaged 2.4 out of 5.</p><p><strong>Late sessions (Week 2):</strong> Average score of 4.0, with best sessions hitting 4.67. Evidence scores climbed from 2.4 to 4.0 &#8212; a 67% improvement. The AI now gathers logs and production data before making claims, acknowledges when evidence is inconclusive, and runs verification before declaring success.</p><p><strong>What drove the improvement:</strong> Three runs of <code>/improve</code> in a single afternoon analyzed 17 patterns across historical sessions, identified the actionable ones, and applied fixes to three different skills. Each fix was small &#8212; a sentence or two added to a workflow document. But the cumulative effect was dramatic.</p><p>The pattern that matters: I didn&#8217;t improve the AI. I improved the instructions the AI follows. The AI is the same model. The system around it is different.</p><h2>What this taught me about AI-assisted product work</h2><p>Most PMs I talk to are still in the &#8220;tune the prompt&#8221; phase. They&#8217;re trying to get better output by being more specific about what they want, giving more context, choosing the right moment to ask. That works, up to a point. But it doesn&#8217;t compound. Next session, you&#8217;re tuning again.</p><p>The shift that changed my productivity wasn&#8217;t a better prompt. It was treating my AI workflow as a system with measurable quality, feedback loops, and the ability to improve itself. Three things made the difference:</p><p><strong>Structured workflows beat ad-hoc prompting.</strong> A seven-phase ideation pipeline with quality gates between phases produces more consistent output than &#8220;help me think through this feature.&#8221; The gates &#8212; like requiring quantitative success criteria before design begins &#8212; catch the skipped steps that ad-hoc prompting misses.</p><p><strong>Evaluation is the foundation of improvement.</strong> If you can&#8217;t measure whether the AI followed the process, you can&#8217;t improve the process. The six-dimension scoring framework gave me visibility into patterns I couldn&#8217;t see from individual sessions. &#8220;Verification is flat at 3.4&#8221; is actionable. &#8220;The AI sometimes doesn&#8217;t check its work&#8221; is not.</p><p><strong>Small rules compound faster than big redesigns.</strong> The self-assessment gate. The &#8220;don&#8217;t declare root cause without production evidence&#8221; rule. The &#8220;acknowledge uncertainty instead of overstating confidence&#8221; instruction. Each one is a sentence or two. Together, they drove a 30% quality improvement in two weeks. The system gets better every time someone uses it and feeds back what went wrong.</p><h2>Building your own</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sK4w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sK4w!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png 424w, https://substackcdn.com/image/fetch/$s_!sK4w!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png 848w, https://substackcdn.com/image/fetch/$s_!sK4w!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png 1272w, https://substackcdn.com/image/fetch/$s_!sK4w!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sK4w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png" width="1456" height="1312" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1312,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The four-layer architecture: context, skills, evaluation, feedback loops&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The four-layer architecture: context, skills, evaluation, feedback loops" title="The four-layer architecture: context, skills, evaluation, feedback loops" srcset="https://substackcdn.com/image/fetch/$s_!sK4w!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png 424w, https://substackcdn.com/image/fetch/$s_!sK4w!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png 848w, https://substackcdn.com/image/fetch/$s_!sK4w!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png 1272w, https://substackcdn.com/image/fetch/$s_!sK4w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77a8ee2c-7d6e-473e-89a5-cce09a8f276c_1800x1622.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The specific workbench I built is internal &#8212; tuned to our systems, our data, our business. But the process is applicable to any project using AI. What matters isn&#8217;t the specific skills &#8212; it&#8217;s the four-layer architecture:</p><ol><li><p><strong>Context</strong> &#8212; codebase understanding, a data semantic layer, and business heuristics that ground the AI in your world</p></li><li><p><strong>Skills</strong> &#8212; structured workflows that break complex PM tasks into phases with quality gates and concrete artifacts</p></li><li><p><strong>Evaluation</strong> &#8212; a scoring framework that measures process adherence across dimensions you care about, logged over time</p></li><li><p><strong>Feedback loops</strong> &#8212; a mechanism that reads evaluation patterns and modifies skills to prevent recurring failures</p></li></ol><p>You can build this in any AI coding tool that supports custom instructions or skills &#8212; Claude Code, Cursor, or whatever comes next. It applies whether you&#8217;re building a product, writing code, doing research, or managing a team with AI. The model doesn&#8217;t matter as much as the system around it.</p><p>Start with one workflow you repeat often. Write down the steps. Add a gate where the AI tends to skip ahead. Measure whether it follows the gate. When it doesn&#8217;t, strengthen the instruction. Repeat.</p><p>There&#8217;s a limit to this approach I haven&#8217;t solved. The system improves the AI&#8217;s process, but it can&#8217;t improve the AI&#8217;s judgment. When a decision requires taste &#8212; which scope to cut, which trade-off to accept, which user pain matters most &#8212; no amount of workflow engineering helps. The system gets you to the decision faster and with better evidence, but the decision is still yours.</p><p>That&#8217;s probably the right boundary. The system handles the process so you can focus on the judgment. But I&#8217;d be lying if I said I knew exactly where that line is. I&#8217;m still finding it.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.oakheartlab.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Oakheart Lab! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[The bottleneck nobody talks about: why product leaders ration their curiosity]]></title><description><![CDATA[What changed when the cost of a data question dropped from two weeks to two minutes]]></description><link>https://www.oakheartlab.com/p/the-bottleneck-nobody-talks-about</link><guid isPermaLink="false">https://www.oakheartlab.com/p/the-bottleneck-nobody-talks-about</guid><dc:creator><![CDATA[Yilun Zhang]]></dc:creator><pubDate>Thu, 16 Apr 2026 12:03:19 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/aca2553d-4cf4-4314-9264-9c888e167506_2400x1260.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Product is a weird job. You&#8217;re accountable for outcomes, but you don&#8217;t manage the teams that deliver them. You sit across engineering, design, analytics, ops, marketing, and you need all of them moving in the same direction.</p><p>The way you earn that influence is by being useful. By being the person who consistently shows up with something nobody else had looked at, and saying &#8220;I dug into this&#8221; in a way that changes the conversation.</p><p>Backing up your hypothesis with insightful data is the best way to earn that trust. AI has transformed my workflow here more than anything else I&#8217;ve adopted &#8212; I went from waiting two weeks for an analyst to run a query to answering my own questions in minutes.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.oakheartlab.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.oakheartlab.com/subscribe?"><span>Subscribe now</span></a></p><h2>The bottleneck nobody talks about</h2><p>At most large companies, the analytics cycle works like this: PM writes a question. Analyst interprets it (often differently than intended). Analyst writes a query. PM reviews the results. Results raise more questions. Another round trip. Two weeks later, you have a number you&#8217;re 60% confident in.</p><p>This isn&#8217;t the analyst&#8217;s fault. They&#8217;re good at their job. The problem is structural: every question costs a round trip, so you learn to ask fewer questions. You go into meetings with your best guess instead of an answer. You end up arguing from conviction when you&#8217;d rather be arguing from evidence.</p><p>When the cost of a question drops from two weeks to two minutes, you stop rationing your curiosity. You ask the follow-up. You check the adjacent thing. The questions themselves get better.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bi0c!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bi0c!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png 424w, https://substackcdn.com/image/fetch/$s_!bi0c!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png 848w, https://substackcdn.com/image/fetch/$s_!bi0c!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!bi0c!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bi0c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The analytics cycle before and after: 2 weeks of PM-analyst round trips vs minutes of conversational iteration with AI&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The analytics cycle before and after: 2 weeks of PM-analyst round trips vs minutes of conversational iteration with AI" title="The analytics cycle before and after: 2 weeks of PM-analyst round trips vs minutes of conversational iteration with AI" srcset="https://substackcdn.com/image/fetch/$s_!bi0c!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png 424w, https://substackcdn.com/image/fetch/$s_!bi0c!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png 848w, https://substackcdn.com/image/fetch/$s_!bi0c!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!bi0c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4730fa5-8e7e-42bd-95b1-50e4acdd5e1e_1600x1200.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>The first version failed for an interesting reason</h2><p>My first attempt was straightforward: connect an AI to our Databricks warehouse, give it a data catalog in a YAML file, and let it write SQL. I built it in Cursor over a couple of days.</p><p>The problem surfaced immediately: the AI didn&#8217;t know what the data meant. It could write syntactically correct SQL, but it would confidently query the wrong columns and produce plausible answers that were just wrong. The data catalog I&#8217;d manually written was too thin. It described table names and column types but not the business logic, the gotchas, or the relationships between systems.</p><p>This is the failure mode that most &#8220;just connect AI to your database&#8221; tutorials skip entirely. The AI doesn&#8217;t know that <code>chkin_dt</code> is the field you use for revenue analysis, not <code>reservation_dt</code>. It doesn&#8217;t know that certain status codes mean different things depending on which system generated them. It doesn&#8217;t know that one table was deprecated six months ago but still has data flowing into it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-cB2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-cB2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png 424w, https://substackcdn.com/image/fetch/$s_!-cB2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png 848w, https://substackcdn.com/image/fetch/$s_!-cB2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!-cB2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-cB2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;What the AI sees vs what it needs to know: raw schema vs semantic catalog&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What the AI sees vs what it needs to know: raw schema vs semantic catalog" title="What the AI sees vs what it needs to know: raw schema vs semantic catalog" srcset="https://substackcdn.com/image/fetch/$s_!-cB2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png 424w, https://substackcdn.com/image/fetch/$s_!-cB2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png 848w, https://substackcdn.com/image/fetch/$s_!-cB2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!-cB2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee553be7-90bc-4229-962a-47474f6195c1_1600x1200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>What actually worked</h2><p>The fix was building a separate layer that discovers and documents the data automatically. An agent crawls the data warehouse, profiles tables, cross-references column usage with the actual codebase, and builds a catalog that captures semantics, not just schema. What the data means. How it&#8217;s used. What the known issues are.</p><p>We tested this. AI analysis quality with just a basic connection scored 2.4 out of 10. With the discovery catalog layered in, the same questions scored 6.3 out of 10. Analytical depth jumped 600%. Methodology improved 167%. Recommendation quality went up 250%.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nUVy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nUVy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png 424w, https://substackcdn.com/image/fetch/$s_!nUVy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png 848w, https://substackcdn.com/image/fetch/$s_!nUVy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!nUVy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nUVy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;AI analysis quality: 2.4/10 with basic schema, 6.3/10 with semantic catalog&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="AI analysis quality: 2.4/10 with basic schema, 6.3/10 with semantic catalog" title="AI analysis quality: 2.4/10 with basic schema, 6.3/10 with semantic catalog" srcset="https://substackcdn.com/image/fetch/$s_!nUVy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png 424w, https://substackcdn.com/image/fetch/$s_!nUVy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png 848w, https://substackcdn.com/image/fetch/$s_!nUVy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!nUVy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6285a358-7e46-438d-9d66-326a3d3b2d55_1600x1200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The catalog also learns. When someone runs an investigation and discovers something new about the data (a join that works, a field that&#8217;s misnamed, a table that&#8217;s more reliable than the documented alternative), that knowledge feeds back in.</p><p>6.3 isn&#8217;t perfect. The agent sometimes misses relevant catalog context that&#8217;s right there. Long investigations go sideways &#8212; it&#8217;ll chase a dead end when a human would have pivoted minutes ago. And it still presents results with more confidence than the data warrants, despite every guardrail I&#8217;ve put in place.</p><p>But 6.3 is usable. I can sit down with the AI, have an actual data exploration conversation, steer it when it drifts, and with some back-and-forth, surface insights that are genuinely interesting. That&#8217;s a different world from 2.4.</p><h2>What changes when you can answer your own questions</h2><p>I can now go from a half-formed business question to a trustworthy answer in minutes. But the speed is almost beside the point. What actually changed is that I stopped rationing.</p><p>Within the first few weeks, I&#8217;d found a customer care cost pattern that nobody had connected across systems. Separately, I noticed early return behavior pointing to a fixable issue in our policies. One investigation into rate policy surfaced a potential impact worth tens of millions of dollars &#8212; something that had been sitting in the data but never came up because nobody had asked the right sequence of questions.</p><p>None of these were questions anyone had thought to ask. They came from following one answer to the next question, the kind of unrationed curiosity that the old two-week cycle made impossible. When you can iterate in real time, you notice things that sequential analysis misses.</p><p>You don&#8217;t need to persuade people that your hunch is right when you can show them something they didn&#8217;t know. The data does the work for you.</p><p>The analysts benefit too. When I&#8217;m not consuming their bandwidth with basic queries, they do the deeper statistical work and modeling that actually requires their expertise.</p><h2>How to start</h2><p>You don&#8217;t need to build what I built.</p><p>Start by connecting AI to your warehouse. Most cloud data platforms (Databricks, Snowflake, BigQuery) have APIs or MCP connectors that let an AI agent run read-only queries. It&#8217;s read-only, so the risk is low. The worst case is a wrong answer, which is the same worst case you already have with human analysts.</p><p>Then write down what the AI gets wrong. The first time it confidently queries the wrong column, note which column it should have used and why. First time it misinterprets a status code, document the correct interpretation. That&#8217;s your data catalog. It doesn&#8217;t need to be a product. A markdown file works. You can get this far in a weekend.</p><p>After that, automate the discovery. An agent that profiles your tables, checks how columns are actually used in application code, and enriches the catalog without you writing every entry by hand. This is what took us from 2.4 to 6.3, and it&#8217;s where the investment starts paying off.</p><p>Good insight is viral inside a company. When you surface something that changes a decision, people notice. The more calls you can back with evidence, the more your cross-functional partners trust your judgment &#8212; not because you positioned yourself well, but because you were genuinely useful. And it starts with the simplest possible shift: stop rationing your curiosity.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.oakheartlab.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Oakheart Lab! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Subagent-driven development: how to parallelize AI agent without blowing up your codebase]]></title><description><![CDATA[What a 69-file commit, a production 401, and an outdated spec taught me about parallel AI coding agents]]></description><link>https://www.oakheartlab.com/p/subagent-driven-development-how-to</link><guid isPermaLink="false">https://www.oakheartlab.com/p/subagent-driven-development-how-to</guid><dc:creator><![CDATA[Yilun Zhang]]></dc:creator><pubDate>Mon, 13 Apr 2026 14:03:35 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e883e313-56e5-458d-b791-c90f0729bbf0_2400x1260.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last month I ran a 17-story implementation across parallel AI agents and compressed roughly 18 hours of sequential work into under 4 hours. Three independent modules that would have taken 14 minutes back-to-back finished in 6 minutes &#8212; wall-clock time bounded by the slowest agent, not the sum. A 300-table exploration job went from 20 hours to 4.</p><p>Getting there involved a 69-file commit where only 4 files were on-topic, a production 401 that traced back to silently overwritten config edits, and an agent that built an entire feature against an outdated spec. Parallelizing AI agents isn&#8217;t hard. Doing it without breaking things is the actual problem.</p><p>Each of these failures taught me something about a different layer of the problem. The first is about isolation &#8212; keeping agents from stepping on each other&#8217;s files. The second is about consistency &#8212; making sure agents see the latest state. The third is about verification &#8212; not trusting any single agent&#8217;s claim that the work is done.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.oakheartlab.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Oakheart Lab! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h2>The orchestrator pattern</h2><p>One coordinator agent manages a task board. Worker agents each implement one story, commit it, run tests, and report back. The coordinator never writes code. It figures out what depends on what, dispatches workers, and won&#8217;t start the next wave until the previous wave&#8217;s tests are green.</p><pre><code><code>Coordinator
&#9500;&#9472;&#9472; Task A: new data model (creates models.py)  &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9500;&#9472;&#9472; Task B: query optimizer (edits optimizer.py)       &#9474;&#9472;&#9472; Wave 1: dispatch simultaneously
&#9500;&#9472;&#9472; Task C: config module (creates config.py)  &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
&#9474;
&#9492;&#9472;&#9472; Task D: retry logic (edits optimizer.py) &#9472;&#9472;&#9472;&#9472; Wave 2: blocked until Task B passes
</code></code></pre><p>The interesting part isn&#8217;t the implementation. It&#8217;s the dependency analysis. Before dispatching anything, you build the graph. Tasks that touch independent files with no shared state can run concurrently. Tasks that share files have to wait.</p><p>In practice, across a 17-task run, this produced batches like: - Tasks 1 + 2 + 3 simultaneously (all new files, zero overlap) - Tasks 4 + 5 simultaneously (different modules) - Tasks 6 + 7 + 8 simultaneously (pure utilities, no shared state)</p><p>The orchestrator&#8217;s job at each step is just: &#8220;what&#8217;s unblocked right now?&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ImDG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ImDG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png 424w, https://substackcdn.com/image/fetch/$s_!ImDG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png 848w, https://substackcdn.com/image/fetch/$s_!ImDG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png 1272w, https://substackcdn.com/image/fetch/$s_!ImDG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ImDG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png" width="1456" height="1225" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1225,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Dependency graph showing tasks dispatched in parallel waves. Wave 1 has three tasks editing independent files, all dispatched at once. A gate labeled \&quot;ALL TESTS PASS\&quot; blocks Wave 2 until Wave 1 is done. A dashed arrow shows Task D must wait for Task B because they share optimizer.py.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Dependency graph showing tasks dispatched in parallel waves. Wave 1 has three tasks editing independent files, all dispatched at once. A gate labeled &quot;ALL TESTS PASS&quot; blocks Wave 2 until Wave 1 is done. A dashed arrow shows Task D must wait for Task B because they share optimizer.py." title="Dependency graph showing tasks dispatched in parallel waves. Wave 1 has three tasks editing independent files, all dispatched at once. A gate labeled &quot;ALL TESTS PASS&quot; blocks Wave 2 until Wave 1 is done. A dashed arrow shows Task D must wait for Task B because they share optimizer.py." srcset="https://substackcdn.com/image/fetch/$s_!ImDG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png 424w, https://substackcdn.com/image/fetch/$s_!ImDG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png 848w, https://substackcdn.com/image/fetch/$s_!ImDG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png 1272w, https://substackcdn.com/image/fetch/$s_!ImDG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0692a0e-f390-4093-8e57-302c0a1b3b0b_1600x1346.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Layer 1: Isolation &#8212; keeping agents out of each other&#8217;s files</h2><p>When multiple AI coding sessions share a working directory, <code>git add -A</code> becomes a landmine.</p><p>What happened: eval scores started degrading mysteriously. Turned out a recent commit had 69 files in it, but only 4 were relevant. The other 65 were artifacts from other Claude Code sessions running in the same repo. Ideation packages, security reports, test fixtures, completely unrelated stuff.</p><p>Same week, different problem: an orchestrator makes two inline config edits, then dispatches a subagent to implement a new endpoint. The subagent rewrites the config file from scratch as part of its task. The inline edits are gone. The merge goes to main. Production returns 401s.</p><p>Both problems have the same root cause: agents sharing a workspace without isolation. The fix is git worktrees. Each session gets its own working directory and branch. The git object database is shared (concurrent reads and writes are fine), but file-level changes are completely isolated.</p><pre><code><code># Each worktree = separate directory + separate branch
.claude/worktrees/
  &#9500;&#9472;&#9472; feature-auth/          # branch: worktree-feature-auth
  &#9500;&#9472;&#9472; watchdog-agent/        # branch: worktree-watchdog-agent
  &#9492;&#9472;&#9472; data-pipeline/         # branch: worktree-data-pipeline
</code></code></pre><p>Two additional rules that prevent the remaining edge cases:</p><p>First, block broad staging commands with a hook so no agent can accidentally sweep in unrelated files:</p><pre><code><code>{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "if echo \"$TOOL_INPUT\" | grep -qE 'git add -A|git add \\.|git add --all'; then echo 'ERROR: Broad staging blocked. Use explicit file paths.'; exit 1; fi"
      }]
    }]
  }
}
</code></code></pre><p>Second, commit before dispatch. Any inline changes that a subsequent subagent might touch have to be committed before that subagent runs. Subagents read files from disk, not from your in-session state.</p><pre><code><code>git add backend/middleware/auth.py backend/config.py
git commit -m "chore: pre-dispatch baseline for auth changes"
# Now dispatch the subagent safely
</code></code></pre><p>If two worktrees both spin up integration tests, they&#8217;ll collide on <code>localhost:8000</code>. Fix that with port offsetting per worktree:</p><pre><code><code>BACKEND_PORT = int(os.environ.get("TEST_PORT_OFFSET", 0)) + 8000
TEST_PORT_OFFSET=1 claude  # uses 8001
TEST_PORT_OFFSET=2 claude  # uses 8002
</code></code></pre><div><hr></div><h2>Layer 2: Consistency &#8212; making sure agents see the latest state</h2><p>Isolation solves the file-collision problem, but it creates a new one. If Agent A and Agent B are both editing <code>config.py</code> in separate worktrees, they&#8217;re fully isolated &#8212; but Agent B might be working from an outdated version.</p><p>This one is insidious because nothing errors out. Agent B reads <code>config.py</code> at the start of its task. Meanwhile, Agent A is rewriting that same file. Agent B finishes and commits. Agent A finishes and commits on top. There&#8217;s no merge conflict because they edited different sections. The code compiles. Tests pass. The logic is wrong.</p><p>I hit this when architecture docs drifted from the actual implementation. An agent was building features against the spec, but the spec described parameter names and feature flags that had changed during prototyping. The agent&#8217;s work was internally consistent but externally wrong.</p><p>The fix comes in two parts:</p><p>First, prefer new files over shared edits. The safest parallel tasks create new files rather than editing existing ones. When agents create files, there&#8217;s zero chance of reading stale state, and parallel execution is actually safer than sequential.</p><p>Second, when agents must share files, sequence them explicitly and commit between each handoff.</p><pre><code><code>Agent A edits config.py &#8594; commits
Agent B reads config.py &#8594; sees A's changes &#8594; proceeds
</code></code></pre><p>Not:</p><pre><code><code>Agent A starts editing config.py
Agent B reads config.py        &#8592; sees the OLD version
Agent A commits
Agent B commits                &#8592; based on stale state, no conflict
</code></code></pre><p>The dependency graph from the orchestrator pattern handles this automatically: if two tasks touch the same file, they go in different waves. The gate between waves forces a commit, so the next wave always reads the latest state.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IQVZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IQVZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png 424w, https://substackcdn.com/image/fetch/$s_!IQVZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png 848w, https://substackcdn.com/image/fetch/$s_!IQVZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png 1272w, https://substackcdn.com/image/fetch/$s_!IQVZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IQVZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png" width="1456" height="972" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:972,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Timeline showing how stale context creeps in. Agent A edits config.py and commits v2, but Agent B already read the old v1 and is working from outdated state. No errors, no merge conflicts &#8212; the logic is just wrong.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Timeline showing how stale context creeps in. Agent A edits config.py and commits v2, but Agent B already read the old v1 and is working from outdated state. No errors, no merge conflicts &#8212; the logic is just wrong." title="Timeline showing how stale context creeps in. Agent A edits config.py and commits v2, but Agent B already read the old v1 and is working from outdated state. No errors, no merge conflicts &#8212; the logic is just wrong." srcset="https://substackcdn.com/image/fetch/$s_!IQVZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png 424w, https://substackcdn.com/image/fetch/$s_!IQVZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png 848w, https://substackcdn.com/image/fetch/$s_!IQVZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png 1272w, https://substackcdn.com/image/fetch/$s_!IQVZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27fad96a-69e9-49de-9773-7aa20bcd9feb_1800x1202.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Layer 3: Verification &#8212; not trusting any agent&#8217;s word</h2><p>Isolation and consistency prevent agents from corrupting each other&#8217;s work. But they don&#8217;t prevent a single agent from shipping a bad implementation. That requires a review gate.</p><p>Don&#8217;t just dispatch implementers. Dispatch a spec review agent after each implementation, before marking the task complete. The reviewer reads the spec and the diff, flags gaps, and feeds findings to a fix agent.</p><p>One thing that tripped me up: completed subagents can&#8217;t receive follow-up messages. So the fix cycle is spin up a new fix agent with the review findings, not try to resume the original implementer.</p><pre><code><code>Implementer Agent &#8594; commits code, reports done
Spec Review Agent &#8594; reads spec + diff, outputs gap report
Fix Agent (if needed) &#8594; receives gap report, patches and recommits
Orchestrator &#8594; marks task complete only after review passes
</code></code></pre><p>This caught a missing empty-list test case, an off-by-one in chunk boundary logic, and a config key that was implemented but not exposed in the public interface. All before the stories were marked done.</p><p>Without the review gate, these bugs would have shipped silently. The implementer agent would have reported success (it always does), and the orchestrator would have moved on. The review agent is what turns &#8220;the code compiles&#8221; into &#8220;the code is correct.&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!w53G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!w53G!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png 424w, https://substackcdn.com/image/fetch/$s_!w53G!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png 848w, https://substackcdn.com/image/fetch/$s_!w53G!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png 1272w, https://substackcdn.com/image/fetch/$s_!w53G!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!w53G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png" width="1456" height="993" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:993,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Task lifecycle showing two paths. Happy path: Dispatch &#8594; Implement &#8594; Commit &#8594; Review &#8594; Done. When review finds gaps: the gap report goes to a fresh fix agent (because completed agents can't receive messages), which recommits and loops back to review until all issues are resolved.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Task lifecycle showing two paths. Happy path: Dispatch &#8594; Implement &#8594; Commit &#8594; Review &#8594; Done. When review finds gaps: the gap report goes to a fresh fix agent (because completed agents can't receive messages), which recommits and loops back to review until all issues are resolved." title="Task lifecycle showing two paths. Happy path: Dispatch &#8594; Implement &#8594; Commit &#8594; Review &#8594; Done. When review finds gaps: the gap report goes to a fresh fix agent (because completed agents can't receive messages), which recommits and loops back to review until all issues are resolved." srcset="https://substackcdn.com/image/fetch/$s_!w53G!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png 424w, https://substackcdn.com/image/fetch/$s_!w53G!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png 848w, https://substackcdn.com/image/fetch/$s_!w53G!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png 1272w, https://substackcdn.com/image/fetch/$s_!w53G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7501e7-dd29-4751-a07d-60a40f6bb9d4_2000x1364.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><p>That&#8217;s the whole framework: isolate with worktrees, keep state consistent with commit-between-handoff discipline, and verify with review agents that don&#8217;t trust the implementer&#8217;s word. Before dispatching parallel agents, build the dependency graph, block <code>git add -A</code>, commit your baseline, prefer new files over shared edits, and gate every task on review plus tests. The speedup is just parallelism. The hard part is the discipline to do it safely.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.oakheartlab.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Oakheart Lab! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>