<?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[The Computist Journal]]></title><description><![CDATA[Educational articles, essays, and tutorials on Computer Science research, practice, and education.]]></description><link>https://blog.apiad.net</link><image><url>https://substackcdn.com/image/fetch/$s_!qNGT!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F582c72c0-c120-4ea8-ae6b-376a025250bb_1024x1024.png</url><title>The Computist Journal</title><link>https://blog.apiad.net</link></image><generator>Substack</generator><lastBuildDate>Fri, 05 Jun 2026 01:32:50 GMT</lastBuildDate><atom:link href="https://blog.apiad.net/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Alejandro Piad Morffis]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[apiad@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[apiad@substack.com]]></itunes:email><itunes:name><![CDATA[Alejandro Piad Morffis]]></itunes:name></itunes:owner><itunes:author><![CDATA[Alejandro Piad Morffis]]></itunes:author><googleplay:owner><![CDATA[apiad@substack.com]]></googleplay:owner><googleplay:email><![CDATA[apiad@substack.com]]></googleplay:email><googleplay:author><![CDATA[Alejandro Piad Morffis]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Pseudocode is a lie]]></title><description><![CDATA[And what I use instead]]></description><link>https://blog.apiad.net/p/pseudocode-is-a-lie</link><guid isPermaLink="false">https://blog.apiad.net/p/pseudocode-is-a-lie</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Thu, 04 Jun 2026 11:01:18 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>Monday&#8217;s post was about a single algorithm &#8212; Union-Find, &#945;(n) &#8776; constant. This is the technical companion: what it took to publish that algorithm. Both pieces are part of the June sprint for <a href="https://matcom.github.io/codex/">The Algorithm Codex</a>, which you can also get in print at <a href="https://apiad.gumroad.com/l/codex">Gumroad</a> or as part of the <a href="https://apiad.gumroad.com/l/compendium">Compendium</a>. More at the end.</em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="6000" height="4000" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:4000,&quot;width&quot;:6000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A kangaroo is eating from someone's hand.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&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="A kangaroo is eating from someone's hand." title="A kangaroo is eating from someone's hand." srcset="https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1747979992703-d85e27eea977?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4NHx8bGllfGVufDB8fHx8MTc4MDUxMDM2NHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 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><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@ellephant_photography">Ellephant</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>Pseudocode is informal enough to be ambiguous and formal enough to be annoying. Every advantage it claims is better served by something else.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>Three claims for pseudocode, all false</h2><p>The canonical defense of pseudocode is <em>Introduction to Algorithms</em> (Cormen, Leiserson, Rivest, and Stein), known as CLRS. Thirty years. Four generations of computer science students. The single most widely-sold technical book in the field. If you&#8217;ve studied algorithms, you learned from CLRS or from someone who did. And CLRS uses pseudocode throughout, defends it explicitly, and makes the case as well as it can be made.</p><p>The argument for pseudocode rests on three things. First, it hides details that don&#8217;t matter for the explanation &#8212; variable types, secondary operations, boilerplate. Second, it speaks at the same conceptual level as the narrative; you can invent operations, give them names, use notation that fits the idea you&#8217;re explaining. Third, it survives time: programming languages rise and fall, but pseudocode is language-agnostic, and a textbook meant to teach the same concepts to students who will write Python, Java, Rust, or something that doesn&#8217;t exist yet can&#8217;t be tied to any of them.</p><p><strong>Problem 1: Pseudocode is not intuitive.</strong> If you want to convey an idea intuitively &#8212; how a process unfolds, why an algorithm works, what the conceptual structure is &#8212; use a diagram, a narrative, an informal sketch. These are genuinely intuitive; they don&#8217;t impose formal constraints. Pseudocode has syntax. It requires <code>for</code>, <code>while</code>, <code>if</code>, formatting conventions, notation that the reader has to parse before they can extract the idea. The claim that pseudocode is easier to read than real code is false. It&#8217;s <em>different</em> from real code, but not simpler. For genuinely intuitive explanation, there are better tools.</p><p><strong>Problem 2: Pseudocode is not formal.</strong> Real code has formal semantics. There is a defined mapping from source to behavior; a compiler or interpreter enforces it. Pseudocode has no such mapping. The reader is left to translate it into whatever language they&#8217;ll actually use, and that translation is entirely implicit. It&#8217;s undocumented and un-enforced. The gap between <em>&#8220;swap the elements at positions i and j&#8221;</em> and <em>&#8220;no, you can&#8217;t, you forgot you were working with an immutable list&#8221;</em> is not a minor translation detail. That gap is where the data structure&#8217;s mutability, the cost model, the choice of representation actually live. If your pseudocode rules were formal enough to specify the translation unambiguously, you&#8217;d have a compiler. It would just be code.</p><p><strong>Problem 3: Pseudocode is not portable.</strong> The language-agnostic argument sounds compelling, but it proves too much. Pseudocode isn&#8217;t language-agnostic in any useful sense. It&#8217;s an <em>additional</em> language the reader has to learn. If you give students pseudocode, you&#8217;re giving none of them something they can run. You&#8217;re asking every reader to translate. Whereas if you pick one real language (a widely-used, readable one), many of your readers understand it directly, and the rest have a formal, precise text to translate from. Pseudocode doesn&#8217;t spare readers translation; it makes all of them translate.</p><p>My prior is strong: intuition first, formalization later. An algorithm, a formula, a mathematical concept: explain it intuitively first, then formally. Pseudocode tries to do both and delivers neither. It isn&#8217;t informal enough to convey ideas; it isn&#8217;t formal enough to prove properties. Pseudocode charges you for formal syntax and gives you no formal guarantee.</p><h2>Two registers: diagrams for intuition, real code for formality</h2><p>Use two kinds of explanation, not one.</p><p>For intuitive content, use diagrams and narration. Diagrams don&#8217;t have to be formal &#8212; informal sketches work fine: an arrow from one box to another, a tree branching as a recursion unfolds, a before/after pair showing what partition does to an array. They explain intuitively, expose the underlying reasoning, and don&#8217;t impose the cost of formal syntax.</p><p>For formal content, use real code. Every supposed advantage of pseudocode &#8212; the ability to invent operations, hide irrelevant detail, speak at a high conceptual level &#8212; exists in real programming languages too. Real languages have abstraction and encapsulation. To defer an operation, stub it: a named function with an empty body or a one-line comment. <code>graph.connect(u, v)</code>, <code>database.search(query)</code>, <code>parser.parse(source)</code>. Real code already has the vocabulary for subjects, predicates, and attributes on those subjects. You don&#8217;t need bespoke notation. You need a sufficiently expressive language.</p><p>That language is Python. Of the widely-used general-purpose languages, Python is the most readable. There are more ergonomic languages (certain Lisps, Haskell, Racket), but their readability advantage comes at the cost of familiarity, and a textbook audience that doesn&#8217;t already know those languages won&#8217;t benefit from them. Python is known, readable, and expressive. There is no good reason not to use Python in a programming book, even a book that isn&#8217;t <em>about</em> Python.</p><h2>Literate programming keeps prose and code from drifting</h2><p>If we&#8217;re using real code anyway, formal and executable, why not make that code a real deliverable? A library with unit tests and an import statement, not just a code block in a document.</p><p>The obstacle: docs and code drift apart when they live in separate files. Change the explanation, and you have to remember to update the code. Change the code, and you have to remember to update the prose. In practice, one always lags the other. Pseudocode dodges this: it never has to match anything, so it&#8217;s never wrong. Real code is always either in sync or not.</p><p>Literate programming is Donald Knuth&#8217;s answer, from his 1984 paper. The original system (WEB, later CWEB) was built around Pascal and C. You wrote a single source file interleaving prose and code; a tool called <code>tangle</code> extracted the code into a runnable program, and a tool called <code>weave</code> rendered the prose into a readable document. The writing and the code are the same artifact. You can&#8217;t let them drift because they&#8217;re literally the same file.</p><p>Modern tools have rediscovered the idea. Jupyter notebooks mix prose and executable code. Quarto does the same at book scale. But neither fully extracts the code into a standalone packageable artifact: the notebook <em>is</em> the program, and you can&#8217;t easily ship it as a library.</p><h2><code>illiterate</code> makes the book and the package the same source</h2><p><code>illiterate</code> closes that gap. A build step extracts the tagged blocks from <code>.qmd</code> files (<code>{export=...}</code>) and assembles them into actual Python source files. The book renders; the package builds from the same source. Three things follow automatically: the documentation can&#8217;t describe an algorithm that doesn&#8217;t compile; the tests run against the same code the reader sees; and the book ships a working library as a side effect of being written.</p><p>In the <a href="https://matcom.github.io/codex/">Algorithm Codex</a> (a hundred algorithms across sorting, graphs, data structures, and dynamic programming), every chapter follows this pattern. No pseudocode anywhere.</p><h3>Quicksort, with the package built from the page</h3><p>A Quicksort chapter opens with the public entry point, the function the reader calls and the one that lands in the package:</p><pre><code><code>```python {export=codex/sorting/quick.py}

from typing import Sequence
from codex.types import Ordering, default_order

def quicksort[T](items: Sequence[T], order: Ordering[T] = default_order) -&gt; list[T]:
    items = list(items)  # don't mutate the input
    _quicksort(items, 0, len(items), order)
    return items
```</code></code></pre><p>The recursive helper is two cases: a base case that bails on slices of length zero or one, and a recursive case that partitions and recurses on both halves:</p><pre><code><code>```python {export=codex/sorting/quick.py}

def _quicksort[T](items: list[T], lo: int, hi: int, order: Ordering[T]) -&gt; None:
    if hi - lo &lt; 2:
        return
    pivot_index = partition(items, lo, hi, order)
    _quicksort(items, lo, pivot_index, order)
    _quicksort(items, pivot_index + 1, hi, order)
```</code></code></pre><p>Both blocks export to the same file; <code>illiterate</code> appends them in document order. Next in that order is the partition step, its own block and its own paragraph. Each conceptual piece earns its own prose frame and its own extracted function.</p><p>When the book renders, Quarto executes every demo block. If any algorithm is wrong, the build fails before that sentence can appear on the page. The build verifies every algorithm in the Codex, every time.</p><p>The book is the primary source. The algorithms are real code that runs. The two have never disagreed.</p><p>Until next time, stay curious.</p><div><hr></div><p><code>illiterate</code><em> is open-source at <a href="https://github.com/apiad/illiterate">github.com/apiad/illiterate</a>, a small Python tool that extracts code from Markdown and assembles it into runnable source files. If you write a book, a tutorial, or a course where the code has to match the text, the setup described here is twenty minutes to replicate.</em></p><p><em>The algorithms it enforces are in <a href="https://apiad.gumroad.com/l/codex">The Algorithm Codex</a>, available in print on Gumroad, or free to read online at <a href="https://matcom.github.io/codex/">matcom.github.io/codex/</a>. Both the Codex and my other books are bundled at <a href="https://apiad.gumroad.com/l/compendium">apiad.gumroad.com/l/compendium</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Start Here: A Map of The Computist Journal]]></title><link>https://blog.apiad.net/p/start-here-a-map-of-the-computist</link><guid isPermaLink="false">https://blog.apiad.net/p/start-here-a-map-of-the-computist</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Mon, 01 Jun 2026 15:24:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!VEFd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VEFd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VEFd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!VEFd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!VEFd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!VEFd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VEFd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1262592,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.apiad.net/i/200136396?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VEFd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!VEFd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!VEFd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!VEFd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55b62031-e273-4b96-8928-5f5637b1327c_1376x768.jpeg 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><figcaption class="image-caption">Generated with <a href="https://github.com/apiad/mosaico">mosaico</a> using Nano Banana 3.</figcaption></figure></div><blockquote><p><em>Last updated: 2026-06-01.</em></p></blockquote><p>If you found this newsletter through a link, a recommendation, or by accident, welcome. This post is the map &#8212; a guided tour of what I write about, why, and where to start.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>What this is about</h2><p>For five years I&#8217;ve been writing about computers &#8212; what they can do, what they can&#8217;t, what they&#8217;re made of, and what they mean. The newsletter started as a side project for sharing things I thought were beautiful and ended up as the place where I think out loud about everything that matters to me professionally.</p><p>I keep coming back to one belief: <strong>the public conversation about AI is broken</strong>. It oscillates between hype and dread, with very little in between. Neither extreme is grounded in what computers actually are. The path through is to look at the machinery &#8212; algorithms, computation theory, formal systems, the long history of what we&#8217;ve learned about what <em>can</em> be computed and what <em>cannot</em>. The map underneath the territory.</p><p>That&#8217;s what this newsletter is for. It&#8217;s also why the writing here ranges from &#8220;what&#8217;s the fastest sorting algorithm&#8221; to &#8220;is the universe a computer.&#8221; Those questions look unrelated. They&#8217;re not. They&#8217;re the same question at different scales &#8212; and the more you know about one, the more sense the others make.</p><p>If you read enough of what I write, you&#8217;ll find I&#8217;m an opinionated technologist with a soft spot for foundational ideas, a deep suspicion of bullshit, and a stubborn belief that <strong>understanding the inside makes you better at navigating the outside</strong>. If that sounds like you, you&#8217;ll like it here.</p><h2>Five paths through the archive</h2><p>The newsletter is loosely organized into five sections. Pick the one closest to what brought you here; each ships roughly two posts a month.</p><h3>&#129302; If you want to understand what AI really is &#8212; and isn&#8217;t</h3><p>This is the largest section, and the one I think about hardest. The thesis: large language models are extraordinary at some things and structurally limited at others, and most of the confusion in public AI discourse comes from not knowing which is which. Three places to start:</p><ul><li><p><strong><a href="https://blog.apiad.net/p/reasoning-llms">The Insurmountable Problem of Formal Reasoning in LLMs</a></strong> &#8212; Or why LLMs still can&#8217;t, and probably never will be able to fully reason. If you only read one post on this newsletter, it should be this one.</p></li><li><p><strong><a href="https://blog.apiad.net/p/reliable-ai-is-harder-than-you-think">Why Reliable AI Requires a Paradigm Shift</a></strong> &#8212; Hallucinations aren&#8217;t a bug we&#8217;ll patch in the next release. They&#8217;re the price of the architecture we&#8217;ve chosen, and the only way out is to change the architecture.</p></li><li><p><strong><a href="https://blog.apiad.net/p/artificial-neural-networks-are-nothing">Artificial Neural Networks Are Nothing Like Brains</a></strong> &#8212; Busting the biggest public misconception in AI. The metaphor is so wrong it actively blocks understanding.</p></li></ul><p>These three together form a kind of mini-syllabus on the real limits of modern AI. They&#8217;re also the spine of my book <em>Mostly Harmless AI</em> &#8212; more on that below.</p><h3>&#129504; If you want to see why computation itself is beautiful</h3><p>I came to AI through computer science, and I think the field has lost something by skipping the foundations. The deepest, most satisfying ideas in computing aren&#8217;t about models or tools &#8212; they&#8217;re about what <em>can</em> be computed and what <em>can&#8217;t</em>. This section is my attempt to make those ideas accessible without dumbing them down.</p><ul><li><p><strong><a href="https://blog.apiad.net/p/the-most-beautiful-algorithms-ever">The Most Beautiful Algorithms Ever Designed</a></strong> &#8212; A very opinionated short list. Read this and you&#8217;ll never look at &#8220;an algorithm&#8221; the same way again.</p></li><li><p><strong><a href="https://blog.apiad.net/p/the-power-of-abstraction">The Power of Abstraction</a></strong> &#8212; How to think like a Computer Scientist. The one mental move that separates programming from computer science.</p></li><li><p><strong><a href="https://blog.apiad.net/p/the-answer-to-all-questions">The Answer to All Questions</a></strong> &#8212; How the quest to answer all mathematical questions became the origin story for a new science. The Hilbert-G&#246;del-Turing arc, told as drama instead of textbook.</p></li></ul><p>If those land, the section has dozens more, from the P vs NP problem to formal language theory to the foundations of machine learning.</p><h3>&#128187; If you want to build something</h3><p>The Mondays of this newsletter are conceptual. The Thursdays are practical. This section is where I show working code, walk through implementation decisions, and occasionally release something into the world.</p><ul><li><p><strong><a href="https://blog.apiad.net/p/chat-with-your-pdf">Chat with your PDF</a></strong> &#8212; A simple streamlit app using LLMs and vector databases to answer questions from a PDF. The minimum viable RAG, demystified.</p></li><li><p><strong><a href="https://blog.apiad.net/p/how-i-built-the-database-of-my-dreams">How I Built the Database of my Dreams</a></strong> &#8212; A pure-Python vector database for prototyping. Build AI apps 100&#215; faster, with one dependency.</p></li><li><p><strong><a href="https://blog.apiad.net/p/realtime-3d-in-pure-python-numpy">Realtime 3D in Pure Python + Numpy</a></strong> &#8212; A side project that went way too far. The kind of thing you write because you want to know if you can.</p></li></ul><p>This section also doubles as the trailer for whatever I&#8217;m currently building &#8212; most of the open-source projects on my GitHub started as a Thursday post.</p><h3>&#10002;&#65039; If you want to argue about the bigger stakes</h3><p>Sometimes I step out of the technical voice and write an opinionated essay. Most of these are about how AI is reshaping science, education, and the way we work &#8212; and what to do about it. None of them have a clean answer; that&#8217;s the point.</p><ul><li><p><strong><a href="https://blog.apiad.net/p/the-techno-pragmatist-manifesto">The Techno-Pragmatist Manifesto</a></strong> &#8212; A level-headed response to techno-pessimists and techno-optimists. Where I plant my flag.</p></li><li><p><strong><a href="https://blog.apiad.net/p/teaching-the-mostly-harmless-way">Teaching the Mostly Harmless Way - Part I</a></strong> &#8212; Principles, practices, and tools I use in all my teaching endeavors. Half autobiography, half pedagogical argument.</p></li><li><p><strong><a href="https://blog.apiad.net/p/the-three-minds-of-a-computer-scientist">The Three Minds of a Computer Scientist</a></strong> &#8212; The Scientist, the Engineer, and the Hacker. A useful taxonomy for anyone in this field.</p></li></ul><p>If you came here from a more humanities-flavored corner of the internet, this section is probably where I sound most like the rest of the writers you read.</p><h3>&#128161; If you want to chase the big questions</h3><p>The deepest section, and the one I&#8217;m most personally invested in. Questions about minds, machines, science, and reality &#8212; through the lens of computation. No clean conclusions; just careful thinking.</p><ul><li><p><strong><a href="https://blog.apiad.net/p/can-machines-think">Can Machines Think?</a></strong> &#8212; What Alan Turing&#8217;s seminal paper <em>Computing Machinery and Intelligence</em> actually says, and what it doesn&#8217;t.</p></li><li><p><strong><a href="https://blog.apiad.net/p/is-science-the-answer-to-life-the">Is Science the Answer to Life, the Universe, and Everything? &#8212; Part I</a></strong> &#8212; Exploring the world of scientific theory and practice. Where epistemology meets engineering.</p></li><li><p><strong><a href="https://blog.apiad.net/p/are-brains-and-computers-alike">Are Brains and Computers Alike?</a></strong> &#8212; The boldest of all theories of mind. A careful walk through the computational theory of mind and its critics.</p></li></ul><p>This section moves slowly &#8212; maybe one post every other month &#8212; but the conversations in the comments are the best on the whole newsletter.</p><h2>The books</h2><p>Across these five paths, certain arguments come back often enough that they grow into books. Right now there are five, in various states:</p><ul><li><p><em><a href="https://store.apiad.net/l/ai">Mostly Harmless AI</a></em> &#8212; the finished one. A book-length argument about what AI can and can&#8217;t do, distilled from the Mostly Harmless AI section. Just shipped v2.0.</p></li><li><p><em><a href="https://store.apiad.net/l/codex">The Algorithm Codex</a></em> &#8212; a growing pay-what-you-want collection of essays on the most beautiful algorithms ever designed. Always in motion.</p></li><li><p><em><a href="https://store.apiad.net/l/tsoc">The Science of Computation</a></em> &#8212; early access. The book version of the Science of Computation section: foundations, computability, complexity, the whole stack.</p></li><li><p><em><a href="https://store.apiad.net/l/graphs">The Hitchhiker&#8217;s Guide to Graphs</a></em> &#8212; early access. Graphs as the universal data structure, and what you can do with them once you take them seriously.</p></li><li><p><em><a href="https://store.apiad.net/l/chatbots">How to Train Your Chatbot</a></em> &#8212; early access. The practical companion to <em>Mostly Harmless AI</em> &#8212; building real LLM applications without the magic-thinking.</p></li></ul><p>If you want them all in one place, the <em><a href="https://store.apiad.net/l/compendium">Computist Compendium</a></em> bundles everything at a discount.</p><h2>The code</h2><p>On top of writing, I maintain a long string of open-source projects: AI tools, web frameworks, Python libraries, and assorted cool toys. Some of them have been quietly running in production at companies I&#8217;ve never heard of; others are weekend experiments that ran away from me. They all live on <a href="https://github.com/apiad">my GitHub</a>.</p><p>The pattern is usually the same: a Thursday post explains why I built the thing, the repo has the code, and a month later it shows up in a Monday essay as a worked example. If you&#8217;re following the build track, watch both feeds.</p><h2>What you get if you subscribe</h2><p>Every article on this newsletter &#8212; every essay, every tutorial, every deep dive &#8212; is free, and will be free forever. I had the incredible fortune of receiving a world-class education at no cost. Writing publicly is my humble way of paying some of that back.</p><p><strong>Starting June 22, 2026</strong> there will also be a paid subscription, for <strong>$7/month</strong> or <strong>$70/year</strong>. Paid subscribers will get:</p><ul><li><p><strong>Every book I&#8217;ve ever published, free, forever.</strong> A 100%-off code refreshed continuously, covering everything in the catalog above and everything I publish from this point onward. At current prices that&#8217;s well over $200 worth of material, and it grows every month.</p></li><li><p><strong>Book drafts as PDFs</strong>, often months before public release &#8212; half-edited, sometimes rough, always the latest thinking.</p></li><li><p><strong>A weekly Friday digest</strong> with what shipped, what&#8217;s coming next week, and an open AMA thread.</p></li><li><p><strong>Prioritized attention</strong> in comments, chat, and Office Hours.</p></li></ul><p>Founding members will lock all of it in for life. If you&#8217;re a professional engineer or researcher, you can almost certainly expense the subscription to your employer &#8212; I&#8217;ll be happy to help you draft the email.</p><h2>What&#8217;s next</h2><p>Every month, a new chapter of a book or a new open-source release. Every Sunday, a short <em>This Week at The Computist Journal</em> note on what shipped and what&#8217;s coming. And every once in a while, an editorial when something in the field is too important to ignore.</p><p>This map gets updated as the archive grows. If you bookmark one post on this newsletter, make it this one &#8212; it&#8217;ll always point you at the current best version of where to start.</p><p>The subscribe button is at the bottom of every post on this site, and the one at the top of this one too. If you&#8217;ve read this far, you already know whether this is your kind of newsletter. Hit it, and I&#8217;ll see you in the next post.</p><p><em>&#8212; Alejandro</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[The fastest algorithm ever devised]]></title><link>https://blog.apiad.net/p/the-fastest-algorithm-ever-devised</link><guid isPermaLink="false">https://blog.apiad.net/p/the-fastest-algorithm-ever-devised</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Mon, 01 Jun 2026 11:30:44 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>Every post on June is related to algorithms, anchored on <a href="https://apiad.gumroad.com/l/codex">The Algorithm Codex</a>. I&#8217;m also running an experiment this month: stretching the pace to one-post-a-day with per-day theme. <strong>Monday</strong> is for <strong>Mind-blowing</strong>. Let&#8217;s see how that goes.</em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="5472" height="3648" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:3648,&quot;width&quot;:5472,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;low angle photography of green trees&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&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="low angle photography of green trees" title="low angle photography of green trees" srcset="https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1462143338528-eca9936a4d09?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHx0cmVlc3xlbnwwfHx8fDE3ODAxMDAzMjF8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 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><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@tbzr">Arnaud Mesureur</a> on <a href="https://unsplash.com">Unsplash</a>. I promise, it will make sense in the end. It&#8217;s a bunch of trees&#8230;</figcaption></figure></div><p>When someone tells you they have the &#8220;fastest algorithm ever devised&#8221;, you probably think they bumped into trick to make clever use of the cache, or something equally tasteless, perhaps Youtube-worthy for 2 hours. I know I&#8217;d do if you came to me with this title.</p><p>But this is not that post. This is about the actual fastest algorithm we have ever devised, and probably the fastest anyone will ever do. And not in a trivial sense. There is one problem in computer science (practical, appearing everywhere you look, explainable in two sentences) where we have an algorithm, a correctness proof, a complexity bound, and a matching lower bound that closes the problem.</p><p>Done. Problem solved. No more papers.</p><p>That kind of closure is the rarest thing in any field of human knowledge.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Four conditions, one winner</h2><p>But what does &#8220;fastest algorithm&#8221; mean? There are many ways you can interpret this phrase, and some of them are trivially true.</p><p>Reading an entry from an array takes constant time. Nothing beats that. But it&#8217;s one operation on a flat structure, no progression, nowhere lower to go. Getting the title should require something harder.</p><p>The interesting frontier is a non-trivial problem with a naive solution everyone reaches for, and another, mindblowingly faster solution that is provably optimal. The problem we analyze today has four traits we want:</p><p>First, <strong>general</strong>: appears in real systems across many domains, not a toy. Second, <strong>explainable in two sentences</strong>: if you need a paragraph to state it, the optimality result arrives too late to matter. Third, <strong>a real progression from naive to optimal</strong>: intermediate ideas that each earned their place, not a single flash of inspiration. Fourth, <strong>provably closed</strong>: algorithm and lower bound meet exactly. Not &#8220;hard to improve.&#8221; Closed.</p><p>Our problem is <em>Union-Find</em>, which clears all four. Nothing else with a non-trivial structure clears them as cleanly. Let me explain it to you in two sentences.</p><h2>You&#8217;ve used this all day</h2><p>Union-Find goes like this. You have <em>n</em> things, organized into groups. You need to merge two groups and ask whether two things are in the same group.</p><p>That&#8217;s it. No deletion. No ordering. No traversal. Two operations on a shifting partition: union and find.</p><p>You&#8217;ve used it today, probably without noticing. Your phone&#8217;s photo app groups every selfie of the same person into a face album by computing connected components, and connected components is what Union-Find does for a living. The bucket tool in any image editor (Paint, Photoshop, Procreate, whatever) floods a region with color by labeling connected pixels? Yes, Union-Find.</p><p>Every time your TypeScript or Rust compiler decides two variables must share a type, Hindley-Milner type inference unifies them through Union-Find. Spanning Tree Protocol, the thing that lets the switches in your office or home network agree on which links to use so packets don&#8217;t cycle forever, runs Kruskal&#8217;s algorithm; Kruskal needs Union-Find to detect cycles.</p><p>The same idea shows up across classical computer science under different names. The canonical image segmentation algorithm (Felzenszwalb and Huttenlocher, 2004) is the bucket tool&#8217;s research-grade cousin. Procedurally generated mazes in games are built by running Kruskal on a grid. Percolation theory in physics, the study of how fluids flow through porous materials, is the same partition-growing-by-merging problem in continuous form. Whenever a system has equivalence classes that grow online, Union-Find is almost always the engine underneath.</p><h2>Union-Find is actually pretty simple</h2><p>The naive solution is straightforward: give every element a label. To merge two groups, sweep through the array and relabel everything in one group with the other&#8217;s identifier. Works perfectly. Linear work per merge. Nothing cheaper to write, and nothing more expensive to run at scale.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fzlO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fzlO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.png 424w, https://substackcdn.com/image/fetch/$s_!fzlO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.png 848w, https://substackcdn.com/image/fetch/$s_!fzlO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.png 1272w, https://substackcdn.com/image/fetch/$s_!fzlO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fzlO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.png" width="1400" height="1262" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/be7ae342-73eb-479d-820e-660fb37449ae_1400x1262.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1262,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:99139,&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://blog.apiad.net/i/199939822?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.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_!fzlO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.png 424w, https://substackcdn.com/image/fetch/$s_!fzlO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.png 848w, https://substackcdn.com/image/fetch/$s_!fzlO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.png 1272w, https://substackcdn.com/image/fetch/$s_!fzlO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7ae342-73eb-479d-820e-660fb37449ae_1400x1262.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><figcaption class="image-caption"><em>Union-Find: n elements, shifting partitions, two operations. Before: three groups. After </em><code>union(x, y)</code><em>: two. Made with <a href="https://apiad.github.io/tesserax">Tesserax</a>.</em></figcaption></figure></div><p>The first idea that earns its keep is a structural change. Stop labeling every element. Give each element a <em>parent pointer</em>: a single reference to another element. The root of each tree is the element that points to itself, and the root names the group.</p><p>To check if two elements share a group: walk each one up its parent pointers to a root. Same root, same group.</p><p>To merge two groups: find both roots and point one at the other. One pointer write, regardless of how many elements are involved.</p><p>The trade has moved. Merge is cheap. But your find now costs as much as the tree is tall.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p6Jv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p6Jv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.png 424w, https://substackcdn.com/image/fetch/$s_!p6Jv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.png 848w, https://substackcdn.com/image/fetch/$s_!p6Jv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.png 1272w, https://substackcdn.com/image/fetch/$s_!p6Jv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p6Jv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.png" width="1400" height="852" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50a1879b-30e0-4531-831d-ebc958cce481_1400x852.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:852,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:80002,&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://blog.apiad.net/i/199939822?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.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_!p6Jv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.png 424w, https://substackcdn.com/image/fetch/$s_!p6Jv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.png 848w, https://substackcdn.com/image/fetch/$s_!p6Jv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.png 1272w, https://substackcdn.com/image/fetch/$s_!p6Jv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50a1879b-30e0-4531-831d-ebc958cce481_1400x852.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><figcaption class="image-caption"><em>Each group is a tree. Arrows point from children up to parents; the root points to itself. </em></figcaption></figure></div><p>Here is the failure mode. An adversarial union sequence can build a long thin chain: element one points to element two, which points to element three, all the way down. Every find walks the whole chain. Linear again.</p><p>The rest of the story is about keeping the trees short, like, really, really <em>short</em>.</p><h2>Making Union-Find fast, really fast</h2><p>The first fix is the obvious one once you&#8217;ve seen the failure. Track each tree&#8217;s height: its <em>rank</em>. When merging, always hang the shorter tree under the root of the taller. The merged tree is no taller than the taller of its inputs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SvQX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SvQX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.png 424w, https://substackcdn.com/image/fetch/$s_!SvQX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.png 848w, https://substackcdn.com/image/fetch/$s_!SvQX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.png 1272w, https://substackcdn.com/image/fetch/$s_!SvQX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SvQX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.png" width="1400" height="753" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:753,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:88662,&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://blog.apiad.net/i/199939822?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.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_!SvQX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.png 424w, https://substackcdn.com/image/fetch/$s_!SvQX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.png 848w, https://substackcdn.com/image/fetch/$s_!SvQX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.png 1272w, https://substackcdn.com/image/fetch/$s_!SvQX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef94b62-86ae-4170-9c95-66e086119dd1_1400x753.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><figcaption class="image-caption"><em>Union by rank: the shorter tree hangs under the taller root. The merged tree&#8217;s height is unchanged.</em></figcaption></figure></div><p>The counting argument is clean. A tree of rank <em>r</em> must contain at least 2<sup>r</sup> elements. You can prove it by induction on the merge rule. So the rank, which bounds the height, can never exceed log&#8322;(<em>n</em>). Both operations now run in O(log <em>n</em>). A million elements: naive worst case is a million steps; rank-bounded is <em>twenty</em>. </p><p>One integer per node buys us exponential improvement in speed. Read that again.</p><p>Most algorithms textbooks would stop here. The second fix is what changes this from a really fast algorithm to the <em>fastest</em> algorithm ever, period.</p><p>Notice something about the walk-to-root operation. When it finishes, it has <em>discovered</em> the root. Every node it visited could be rewired, right now, on the way back, to point straight at the root.</p><p>The structure flattens itself, in place, every time you ask it a question. The next walk on that path takes one step. Work you already did is work you never have to do again.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RvHV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RvHV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.png 424w, https://substackcdn.com/image/fetch/$s_!RvHV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.png 848w, https://substackcdn.com/image/fetch/$s_!RvHV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.png 1272w, https://substackcdn.com/image/fetch/$s_!RvHV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RvHV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.png" width="1400" height="1011" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1011,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:86470,&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://blog.apiad.net/i/199939822?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.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_!RvHV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.png 424w, https://substackcdn.com/image/fetch/$s_!RvHV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.png 848w, https://substackcdn.com/image/fetch/$s_!RvHV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.png 1272w, https://substackcdn.com/image/fetch/$s_!RvHV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba971c19-0ebc-43f8-ba2a-6e5b2c1150a7_1400x1011.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><figcaption class="image-caption"><em>Path compression: </em><code>find(x)</code><em> collapses the chain into a star. Every walked node now points straight at the root.</em></figcaption></figure></div><p>On its own, this trick is good. Combined with the rank rule, the cost drops below logarithmic, into a regime that barely has a name.</p><p>&#191;How fast is this? Strap on, baby.</p><h2>Union-Find is the fastest algorithm ever devised</h2><p>Robert Tarjan, in 1975, proved the two ideas together give an amortized cost per operation of the <em>inverse Ackermann function</em>, written &#945;(<em>n</em>).</p><p>To understand why that means <em>fast</em>, you need a picture of what you&#8217;re inverting. The Ackermann function grows like hell. Faster than exponentials, faster than towers of exponentials, faster than any function built by ordinary recursion. A(4, 4), only four steps in, already exceeds the number of atoms in the observable universe. It&#8217;s a freaking monster, one of the fastest growing functions ever invented.</p><p>The <em>inverse</em> grows correspondingly <em>sloooowww</em>. For any <em>n</em> less than 2 to the power of 65536 (a number whose decimal representation runs twenty thousand digits), &#945;(<em>n</em>) &#8804; 4. Read that again. For <em>any</em> input you could construct on any computer that will ever be built, the average cost per operation is at most <em>four pointer hops</em>.</p><p>Effectively constant. Call it constant, for practical purposes. But it is <em>not</em> constant. There is a real function in there. That fact is not a technicality. This is not a gap waiting for a smarter trick. It is a permanent lower bound, proved by Fredman and Saks in 1989.</p><p>Michael Fredman and Michael Saks worked in the cell-probe model, which counts how many stored bits any data structure must read to answer a query. They proved that &#937;(&#945;(<em>n</em>)) is the true lower bound for the union-find problem. No structure of any design can answer union and find queries doing less amortized work than &#945;(<em>n</em>).</p><p>Tarjan&#8217;s upper bound says path-compression-plus-union-by-rank does at most &#945;(<em>n</em>) amortized. Fredman and Saks&#8217;s lower bound says no structure can ever do less. The two bounds meet exactly.</p><p><em>Done. Problem solved. No more papers.</em></p><h2>Problem solved is a rare luxury</h2><p>Newton&#8217;s laws work until you approach the speed of light. Quantum mechanics works until you zoom out to cosmological scales. Turbulence in fluids is still open. It has been for a century. The three-body problem is still open. Physics doesn&#8217;t finish; it hands off. Every theory we have is a model that fits data within its domain of validity, and I think we forget how strange that is.</p><p>The Schr&#246;dinger equation describes how electrons move in any atom. For helium (two electrons) it has no closed-form solution. The electron interactions couple (each one affecting all the others) in ways that resist exact treatment. Chemistry approximates, and the gap is permanent, not a waiting-for-faster-computers problem.</p><p>Mathematics does close problems, and famously. Wiles closed Fermat&#8217;s Last Theorem in 1995, after the conjecture had sat open for 358 years. The four-color theorem closed in 1976 (with a computer&#8217;s help, which still rankles some). Poincar&#233; closed in 2003. But the closures cluster on problems mathematicians chose: beautiful, deep, and often distant from anything that runs in your hand.</p><p>Computer science almost never gets all three at once. A problem that is practical, used in real systems every day; crisply specified, fitting in two sentences; and provably closed, algorithm and lower bound matched exactly. Union-Find is in this corner. Comparison-based sorting being &#920;(<em>n</em> log <em>n</em>) is in it. Linear search on unsorted data being &#920;(<em>n</em>) is in it. The list is short.</p><p>Algorithm and lower bound, meeting exactly.</p><p>Done. Problem Solved. No more papers.</p><p>The fastest non-trivial algorithm ever devised takes twenty lines to write, runs in essentially constant time per operation, and is one of the very few things we have ever &#8212; in any field &#8212; entirely finished.</p><p>If that isn&#8217;t freaking mind blowing, I don&#8217;t what is. Until next time, stay curious.</p><div><hr></div><p><em>Union-Find is chapter 14 of <a href="https://apiad.gumroad.com/l/codex">The Algorithm Codex</a>, a book that spells out the algorithms every programmer eventually meets in real, functioning Python &#8212; no pseudocode bullshit, no complicated math.</em></p><p><em>Every chapter ends with the same three questions: is it correct? how efficient? is it optimal? Most chapters land two out of three. A small few land all three. Union-Find is the cleanest example in the book of all three boxes ticked.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/codex&quot;,&quot;text&quot;:&quot;Get the Algorithm Codex&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/codex"><span>Get the Algorithm Codex</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Mostly Harmless AI v2.0 is here!]]></title><link>https://blog.apiad.net/p/mostly-harmless-ai-v20-is-here</link><guid isPermaLink="false">https://blog.apiad.net/p/mostly-harmless-ai-v20-is-here</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Sun, 31 May 2026 11:01:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xDXS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xDXS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xDXS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!xDXS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!xDXS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!xDXS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xDXS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:839252,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.apiad.net/i/199956563?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xDXS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!xDXS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!xDXS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!xDXS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371bb65b-4fc0-42dd-86a2-27cb5b9f97d0_1376x768.jpeg 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><p>Hey! Happy Sunday. Here is a quick note to remind you that <em>Mostly Harmless AI</em>, second edition, draft one, is finished. PDF and EPUB on <a href="https://store.apiad.net/l/ai">Gumroad</a>. Free online reader at <a href="https://books.apiad.net/books/mhai">books.apiad.net</a>. Both live, right now.</p><p>This book is a permanent beta, because the AI story is being created as I write these words and it&#8217;s moving just too damn fast for anyone to put it down cleanly in a book. So don&#8217;t expect the kind of polishing that comes with retrospective looks at settled topics. This isn&#8217;t that kind of book. It&#8217;s raw, but up-to-date. It contains basically everything I know and believe about AI that doesn&#8217;t <em>require</em> code or math to understand it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/ai&quot;,&quot;text&quot;:&quot;Get Mostly Harmless AI - 50% off&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/ai"><span>Get Mostly Harmless AI - 50% off</span></a></p><h2>Sixteen chapters, mechanism-first</h2><p>Three parts, sixteen chapters, around two hundred and sixty pages, almost three hundred footnote citations.</p><p><strong>Part I &#8212; Foundations</strong> walks the mechanisms, from the symbolic systems of the 1950s to the agent loops of 2026. Classical AI, machine learning, deep learning, language modeling, generative AI, agentic AI. If you have ever wondered what <em>attention</em>, <em>gradient descent</em>, or <em>RLHF</em> actually mean (not the marketing line, the mechanism), start here.</p><p><strong>Part II &#8212; Applications</strong> shows how AI is reshaping each field: knowledge work, scientific research, software development, education, creative work, policy. Dual-audience throughout. Experts get practice and gotchas. Non-experts see why their lawyer, their doctor, and their kid&#8217;s teacher are suddenly using these tools.</p><p><strong>Part III &#8212; Dangers</strong> is the honest one. Alignment, the limits of language models, and the harms already in the world today: scammers cloning your mother&#8217;s voice, autonomous weapons that pick their own targets, hiring algorithms that quietly downrank you, jobs evaporating from under people who built careers in them. The existential-risk question is in there too, treated proportionally. </p><p>The book lands on a third position beyond maximalists and doomerists. These systems are powerful enough to demand real responsibility and limited enough that the worry should be about who deploys them, not about the silicon waking up. The future is not predetermined. Neither doomer nor utopian framings are right. We choose, and choices have responsibilities attached.</p><p><a href="https://blog.apiad.net/p/mostly-harmless-ai-the-book-that">The first-draft post from two weeks ago</a> has the longer description if you want more.</p><h2>Free online, or pay for offline</h2><ul><li><p><strong>Read it free online</strong>, in perpetuity, at <a href="https://books.apiad.net/books/mhai">books.apiad.net</a>. Clean typography, gentle dark mode, inline footnotes. No popups, no signup, no tracking. The reader was built for these books specifically.</p></li><li><p><strong>PDF and EPUB on Gumroad</strong> if you want it offline, on your e-reader, on a plane: <a href="https://apiad.gumroad.com/l/ai">apiad.gumroad.com/l/ai</a>. Launch week is 50% off.</p></li></ul><p>If the online reader is free, then why would you buy it? Well, you don&#8217;t <em>have </em>to.  But here is why you may want to do it anyway.</p><p>Writing this book took two years. The second edition took three months of intense rewriting on top of that. If you find value in the work and want more of it to exist (these books, the Computist Library, the journal you are reading), paying for the PDF is how you tell me that. I do not run ads. I do not sell your attention. Every book sale is one person telling me to keep going, and the money buys the months it takes to write the next one.</p><p>If you cannot pay, read it free. No guilt, no caveat. That option exists on purpose.</p><h2>June is algorithms month</h2><p>After today, I am putting AI writing to sleep for a couple of months, on purpose.</p><p>Not the work &#8212; I will still be building AI tools and using these systems every day. But on this journal, starting tomorrow and through July, I am switching to <em>algorithms and core computer science</em>, with <a href="https://apiad.gumroad.com/l/codex">The Algorithm Codex</a> as the spine. The Codex is the next book in the Computist Library, written in parallel with MHAI and ready for center stage. Already 200 or so pages, and 50+ algorithms in, but still very rough around the edges, in dire need of your feedback.</p><p>During June, you can expect daily posts, Monday through Friday, each anchored on a chapter. Why the fastest algorithm ever devised runs in essentially constant time. Why most programmers get binary search wrong. Why no comparison sort can beat n log n, and the loophole that lets you anyway. Why some algorithms become <em>settled facts</em> the way theorems do.</p><p>AI returns to the journal in August. The field will surely have moved a lot by then, and there will be more than a few chapters to retouch.</p><p>So if you come here for the AI material, this book is the most complete thing I have on it &#8212; go read that. If you have been curious about the algorithms side, the next two months will be the best stretch of it I publish all year.</p><p>That&#8217;s all for today. Tomorrow I&#8217;ll meet you again with a brand new article.</p><p>Until then, stay curious.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/ai&quot;,&quot;text&quot;:&quot;Get Mostly Harmless AI - 50% off&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://store.apiad.net/l/ai"><span>Get Mostly Harmless AI - 50% off</span></a></p>]]></content:encoded></item><item><title><![CDATA[Introducing Aegis: the programable multi-agent meta-harness]]></title><description><![CDATA[Or -- I did a thing and I want to show it to you]]></description><link>https://blog.apiad.net/p/introducing-aegis-the-programable</link><guid isPermaLink="false">https://blog.apiad.net/p/introducing-aegis-the-programable</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Fri, 29 May 2026 11:05:05 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em><strong>On May 31 we launch <a href="https://store.apiad.net/l/ai">Mostly Harmless AI</a> v2.</strong> This arc &#8212; how models learn, how agents work, where they break, and what it takes to build something real on top of them &#8212; is now a book, updated for May 2026. Newsletter subscribers get 50% off.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/ai&quot;,&quot;text&quot;:&quot;Get Mostly Harmless AI - 50% off&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/ai"><span>Get Mostly Harmless AI - 50% off</span></a></p></blockquote><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="8192" height="5461" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:5461,&quot;width&quot;:8192,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;two silver swords on shield&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&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="two silver swords on shield" title="two silver swords on shield" srcset="https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1541543975512-86aad5d2cf93?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxzaGllbGR8ZW58MHx8fHwxNzc5OTM3MzY2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 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><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@worldsbetweenlines">Patrick Hendry</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>For the better part of the last two months I&#8217;ve been drilling you with a bunch of inter-connected ideas, all gravitating around the notion of agentic reliability. We started with <a href="https://blog.apiad.net/p/mhai-llms">how these models learn</a>, argued about <a href="https://blog.apiad.net/p/70-years-of-ai-history-in-10-minutes">seventy years of AI history</a>, traced <a href="https://blog.apiad.net/p/its-tokens-all-the-way-down">the strange logic of prediction</a>, dissected <a href="https://blog.apiad.net/p/the-anatomy-of-a-linguistic-ai-agent">what an agent actually is</a>, then spent two posts on the edges: <a href="https://blog.apiad.net/p/how-to-write-a-cli-an-agent-will">how to write tools agents can really use</a>, and <a href="https://blog.apiad.net/p/the-80-ai-reliability-horizon">what happens when you push them to their limits</a>.</p><p>This is the last post of that arc, and it embodies my vision of where this whole Agentic AI thing is going. And to show I&#8217;m putting my money where my mouth is, this post is about the tools I&#8217;m building to bring forth that vision.</p><p>But first, why should you care?</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>Agents aren&#8217;t just for coders</h2><p>Here&#8217;s something the AI tool industry quietly gets wrong: every major agent harness &#8212; <a href="https://claude.ai/code">Claude Code</a>, <a href="https://ai.google.dev/gemini-api/docs/gemini-cli">Gemini CLI</a>, <a href="https://cursor.com/">Cursor</a>, <a href="https://opencode.ai/">OpenCode</a> &#8212; is pitched as a developer tool. And technically, yes, they drive code.</p><p>But code is how agents <em>solve problems</em>, not what the problems are. When you ask an agent to research a topic, draft a document, reorganize a folder, schedule your week, or synthesize a dozen sources into one coherent brief &#8212; none of that is fundamentally a programming task. The coding is incidental. A historical accident of the fact that the people who built these tools happened to be programmers solving programmer problems.</p><p>So if you&#8217;re a researcher, a writer, a manager, a student, a scientist who occasionally touches a terminal &#8212; this piece is for you too.</p><h2>What&#8217;s wrong with existing tools</h2><p>I use Claude Code every day. The harnesses &#8212; Claude Code, Gemini CLI, Cursor, OpenCode &#8212; are genuinely good. The agentic loop is robust. Tool design is sharp. Context management is solved for practical purposes. I&#8217;ve opened ten-thousand-line projects and the agent knows exactly where it left off.</p><p>What&#8217;s missing is not single agent capabilities. It&#8217;s <em>coordination</em>.</p><p>Claude Code runs on Claude. Geminis CLI runs on Gemini. OpenCode allows any provider but you cannot use your existing subscriptions (which are heavily subsidized); you have to pay API rates.</p><p>If you&#8217;re running multiple agents with different models from different providers, you end up with four windows open: one per harness, one per model, one per subscription you&#8217;re already paying for. Different tools with slightly different annoying quirks and ways to do and call the same things.</p><p>But you can still make them collaborate, just not easily.</p><p>Most harnesses support sub-agents, which is a subroutine: the main agent mints a new subagent for a specific call, call it, it runs, it returns, the main agent continues.</p><p>What they don&#8217;t support is a mid-work handoff. Imagine you&#8217;re two hours into a brainstorm with Claude, a question has emerged that you cannot answer without trying some code, and now you want a second agent &#8212; different model, fresher context, perhaps Kimi from OpenCode Zen &#8212; to take over a brief coding session, and then handoff back to Claude what it found so brainstorming can continue.</p><p>That is somewhat achievable with subagents (except they run autonomously and die, you cannot, in most tools, interact with them and steer them into new directions). But what you cannot do, is have Kimi return to Claude, and stay up, waiting (with its full context alive) for a second follow up question to continue exploration.</p><p>That transfer doesn&#8217;t exist today. You have to ask Claude to produce a handoff manually, paste it in Kimi explaining the situation from scratch. The paste its response back to Claude, and so on. You have become a secretary between two AI agents.</p><p>Agents are isolated in all these tools. Two agents working on the same document will overwrite each other. There&#8217;s no locking, no merge protocol, no queue where one drops a task and another picks it up. No broadcast so all agents know when the plan changes. Agents don&#8217;t share a world &#8212; they each have their own private window into one.</p><p>I looked around before building. The closest I found: Conductor, which orchestrates multi-agent workflows &#8212; but only for Claude Code. And T3.codes, which drives any harness, closer to the spirit of what I wanted, but neither has cracked the coordination layer as I envision it.</p><h2>Introducing Aegis</h2><p>So, of course, I had to go and make my own. (Quick digression, if you&#8217;ve been reading this blog for a while you know I love to reinvent wheels if only for the learning experience, but this is a case where I genuinely couldn&#8217;t find something good enough.)</p><p>Here&#8217;s what makes Aegis different to anything out there, embodied in its slogan: the programable multi-agent meta-harness. Let&#8217;s build it from the back.</p><p><strong>Meta-harness</strong>. You&#8217;re already paying for Claude via your Anthropic subscription. Gemini via your Google account. If a new tool wants to drive both, it has two options. It can re-authenticate you through its own layer: API keys, rate limits, and lost subscription benefits. Or it can call the native tools, which already have your credentials.</p><p>Aegis takes the second path. It drives Claude Code over its <code>stream-json</code> protocol, Gemini CLI and OpenCode over the Agent Client Protocol &#8212; calling the binaries on your machine, which already have your auth. It doesn&#8217;t touch your subscriptions. You stop worrying about which model wins this month&#8217;s benchmark.</p><p>And because Aegis calls the native harnesses rather than reimplementing them, it inherits everything they&#8217;ve spent months polishing: the agentic loop, the context management, the permission model. The harness keeps owning tool use, sandboxing, model selection. Aegis owns the layer above &#8212; tabs, routing, delegation &#8212; the things a single-conversation CLI was never built to do.</p><p><strong>Multi-agent.</strong> Aegis provides six inter-agent syncronization primitives, built incrementally one above the others, to give you increasingly more powerful multi-agent capabilities.</p><p>The first primitive is a per-agent inbox. Any agent (including you) can hand another agent a message, that gets enqueued until the end of the current turn. This alone enables solving the problem we were just discussing in the previous section.</p><p>Then, on top of that, they got canvases: markdown files shared across agents with per section looks and callbacks that awake an agent when another finishes writing a section.</p><p>Then we got <em>real</em> terminals. Not an agent calling bash on a subprocess and blocking on its result. A real, shared, fully interactive terminal session that multiple agents can scan, tail, and write to, And you can too. One runs a command; another sees the output in real time and reacts. Or you run the backend and ask your agent to look at the logs when that heissenbug happens.</p><p>So far this allows you to spawn several agents and have them collaborate. But you can also have queues. Any agent can drop a task (a prompt) and the queue auto-spawns an ephemeral agent to take it, potentially calling back the emitter once done. Queues have a maximum cap on parallelism, as well as arbitrary rolling budgets on tokens and dollars so you keep control of how much work is allowed to happen without your supervision.</p><p>Agents can also be added into groups, dynamically created and destroyed on demand, by you or any other agent. Groups have a shared inbox and you can subscribe to them and get notified when the first, any, or all the agents in the group finish. This allows committee-like flows where different agents analyze a problem in parallel.</p><p><strong>Programmable.</strong> And finally, you get workflows. Deterministic Python code that drives agent calls in sequence, with branching, conditionals, and loops. Think skills, but instead a hopeful blob of markdown one agent can choose to interpret as they want, these are composable routines that drive the entire substrate in the exact level of control you desire.</p><p>When you write a complex workflow in natural language, you&#8217;re hoping the agent follows through. It might decide step two is better done differently, skip the commit because something caught its eye, or forget step three entirely. A Python workflow doesn&#8217;t forget: it runs step 1, then step 2, then step 3, and commits. You wrote <code>commit()</code> in the code; the code runs. You get the agent&#8217;s creativity at each step; the program guarantees the steps happen.</p><p>Workflows can be scheduled: declare a cron entry in <code>.aegis.yaml</code> and the substrate fires it while you sleep. They also run across machines: one agent on your laptop can enqueue a task to a remote Aegis instance on a VPS and get the result back in its inbox.</p><p><em>Quick aside</em>: Yesterday, as I was polishing this post, Anthropic announced <em>Dynamic Workflows</em> &#8212; a way to orchestrate long-lived agents over dozens of hours of work. I haven&#8217;t tested it yet, but It seems geared toward the same problem I&#8217;m trying to solve.</p><p>The difference is in the philosophy. Anthropic&#8217;s principle is to give agents as much agency as possible: trust the model, let it decide how to get there. Don&#8217;t get in the way, you stupid human. Tokens go brr. It&#8217;s the reason why all their solutions to problems are humoungos one-shot prompts.</p><p>My philosophy runs the other way. Leave agent creativity where it can do the most good &#8212; in the actual work, not in deciding whether step three happens after step four. The deterministic spine isn&#8217;t a constraint on the agent. Its what makes it work despite agent idiosincrasies, and why it works across all agents and harnesses, regardless of their intrinsic capabilities.</p><p>I&#8217;m using Aegis now for about 50-60% of my coding. It&#8217;s still rough around the edges, but it&#8217;s way more fun to use than any single CLI. There is a lot more in the box, like remote sessions, a built-in file browser, lots of metrics... but this post is already way too long. You&#8217;ll have to check it out on your own. Links at the end.</p><h2>What I intentionally left out</h2><p>Aegis has no native concept of skills. No AGENTS.md injected automatically. No memory system.</p><p>Those things are conventions, and conventions change. What belongs in an AGENTS.md today looks different from what it&#8217;ll look like in six months. Memory systems have a dozen competing designs and no consensus. If I&#8217;d baked any of that in, you&#8217;d be stuck with my choices the moment the community moved on.</p><p>Aegis has a very powerful plugin system instead (I told you, it is programable). You write a pure Python function, drop it on some folder, and it gets called anywhere in the agent lifecycle.</p><p>Want skills that activate on context? Write a plugin. Want a memory system? Write a plugin. Want to inject per-repository knowledge before every session? Write a plugin. The conventions you need are yours to build, and when they change, you change them, not me.</p><h2>Coda</h2><p>Claude Code and Gemini CLI are applications. You open them, use them, close them. Aegis is more than that. It&#8217;s a framework. You build on top of it &#8212; applications that spin up the agentic substrate automatically, pull in whichever harness fits the task, and run without anyone at the keyboard.</p><p>Picture a self-hosted Git forge where pushing a branch triggers agents: one reviews the code, one hunts bugs, one picks up open issues and starts implementing. Everyone on their own worktrees, independent, parallel, coding while you sleep. Push code; agents work.</p><p>That&#8217;s <strong>Sindri</strong>, and I&#8217;m also building it; but that&#8217;s a story for another Friday.</p><p>Aegis is open source: <a href="https://github.com/apiad/aegis">github.com/apiad/aegis</a>. <code>pip install aegis-harness</code> to start.</p><div><hr></div><p><em><strong>May closes the agentic AI arc.</strong> If you want the full conceptual foundation &#8212; model internals, agent architecture, tool design, failure modes &#8212; it&#8217;s in <strong><a href="https://store.apiad.net/l/ai">Mostly Harmless AI v2</a></strong>, launching May 31. Subscribers get 50% off.</em></p><p><em>Also check the <a href="https://store.apiad.net/l/compendium">Compendium</a> &#8212; one fixed price, every educational project I&#8217;ve built or will build, yours in perpetuity. Buy once, get everything.</em></p><p><em><strong>In June: algorithms.</strong> The other half of the computational story &#8212; sorting, searching, graphs, optimization, the classical toolkit that AI didn&#8217;t replace and won&#8217;t. All month, one idea at a time. See you there.</em></p><p><em>Until next time, stay curious.</em></p>]]></content:encoded></item><item><title><![CDATA[Why AI Agents Need Structure]]></title><description><![CDATA[A 5-step framework for solving any]]></description><link>https://blog.apiad.net/p/why-ai-agents-need-structure</link><guid isPermaLink="false">https://blog.apiad.net/p/why-ai-agents-need-structure</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Wed, 27 May 2026 10:43:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vQrT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vQrT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vQrT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!vQrT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!vQrT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!vQrT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vQrT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:735692,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.apiad.net/i/199356169?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vQrT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!vQrT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!vQrT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!vQrT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b579226-2570-417d-82f1-3bab6546883b_1376x768.jpeg 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><figcaption class="image-caption"><em>Actual footage of an AI handing off a research spec to another AI&#8212;it&#8217;ll make sense in the end.</em> (Crazy the kinds things you can see in the wild, huh?)</figcaption></figure></div><blockquote><p><em>Every post this month is on the theme of building AI agents that actually work &#8212; anchored on the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a>, 50% off during early access, where the five-phase structure this post describes is a full chapter with more failure cases, the artifact design patterns, and the context isolation mechanics in detail. You can also <a href="https://books.apiad.net/books/mhai/">read the whole book online for free</a>. More at the end.</em></p></blockquote><p>Last night you give your AI agent a clear task. It worked hard for two hours. YOu woke up to a report that is technically correct and completely useless.</p><p>I&#8217;ve had this experience enough times that I stopped blaming the model years ago. The failure is always structural, and I mean that in a very specific, diagnosable sense.</p><p>AI labs fine-tune language models to produce <em>output</em>, not to question the frame. The whole reward cycle behind how these models are trained pushes them toward helpful completions; nobody in that loop is rewarding a model for pausing mid-task and asking &#8220;have you considered that you might be framing this wrong?&#8221;</p><p><strong>Execution</strong> is the default. That setting works beautifully for atomic tasks: create a note, write a short email, implement a specific function, compare two clearly-defined options. It fails quietly, and expensively, when the real problem isn&#8217;t the task itself but the goal behind it. Here&#8217;s three examples:</p><p>First: you ask an agent to &#8220;implement user authentication&#8221; for your web application. Clear task. The agent gets to work, producing a technically sound implementation using JWT tokens, bcrypt password hashing, and session management.</p><p>Second: you ask an agent to &#8220;write a technical report on renewable energy storage.&#8221; Again, clear task. The agent downloads papers, synthesizes findings, produces a well-structured document.</p><p>Third: a family asks an agent to &#8220;plan our move to a new city.&#8221; Thorough research follows &#8212; neighborhoods, school districts, moving companies, cost estimates. In every case, the output is technically correct.</p><p>In every case, the person who asked ends up with something that doesn&#8217;t quite fit. The gap between what was asked and what was needed &#8212; that gap is what this article is about.</p><p><em>The structure is the problem, not the model.</em> </p><p>Not the model&#8217;s intelligence, not the quality of your prompt, not the temperature setting. The structure of the <em>workflow</em> you handed the agent is the thing that determines whether it answers the right question or a reasonable-sounding wrong one. In this article, I&#8217;ll show you what the right structure is. But to understand <em>why</em> it must be so, we need to see how we got there.</p><p>The first fix the industry reached for was planning. It helped. It wasn&#8217;t enough. Strap on for a story.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>Plan First, Build Second</h2><p>The <em>structure</em> is the problem, not the model. Right? So the first fix the industry reached for was adding a <strong>planning mode</strong>.</p><p>If you&#8217;ve used any of the major agentic coding tools over the past couple of years, you&#8217;ve seen this pattern. There&#8217;s a <em>plan mode</em>: read-only, no side effects, meant for thinking through the task. Then a <em>build mode</em>: where the agent executes.</p><p>The intuition behind it is sound &#8212; separating thinking from doing is genuinely better than collapsing them into a single continuous stream. When you mix planning and execution in the same pass, the agent makes irreversible changes based on its first interpretation of the goal.</p><p>A planning mode forces a pause. You get to inspect the plan, argue with it, revise it before anything is written to disk. The plan is the first concrete artifact in the handoff chain. You can hold it in your hands &#8212; or at least on your screen &#8212; and have a real conversation about it.</p><p>Genuine progress. I don&#8217;t want to minimize it. But watch what happens with the family move.</p><p>The agent produces a thorough plan: compare three target neighborhoods, compare school districts in each, contact five moving companies for quotes, estimate total relocation costs, build a timeline. Excellent plan. The family reviews it, nods along, says &#8220;looks good,&#8221; and the agent executes.</p><p>Six months later: they&#8217;re living in a city that made complete sense on paper &#8212; except the job that drove the whole move turned out to be fully remote. They never needed to relocate at all, or they could have moved somewhere half the price with better schools.</p><p>The plan was correct. The execution was correct. The <em>goal</em> was wrong. And here&#8217;s the point: the agent that built the plan never questioned whether &#8220;plan a move to City X&#8221; was the right goal, because it was never asked to. It took the first reasonable interpretation of the prompt &#8212; &#8220;they want to move, let me help them move well&#8221; &#8212; and planned confidently inside that frame.</p><p>The software auth example is shorter but sharper. &#8220;Implement user authentication&#8221;: the agent plans for JWT tokens, bcrypt hashing, session storage. Fine plan. Sound, even. For a single-tenant web app. This is a multi-tenant SaaS product (one where dozens of customers share the same running application.)</p><p>The plan was never going to catch that, because nobody told the planning agent that multi-tenancy was a constraint. It did exactly what it was asked.</p><p>Now, the objection you might be forming: &#8220;just review the damn plan carefully before you approve it.&#8221; That objection is right about something, review does matter, bit it misses the fact that the agent that produced the plan has already <em>committed</em> to a frame.</p><p>Every question it asks, every tradeoff it surfaces, every option it presents &#8212; all of it is already shaped by its first interpretation of the goal. When you review that plan, you&#8217;re not reviewing a neutral set of options. You&#8217;re reviewing a plan that already selected its own success criteria. The frame is invisible because it was never made explicit.</p><p>Planning without exploration locks in the first reasonable goal.</p><h2>Research Before the Plan</h2><p>Once more, the <em>structure</em> is the problem, not the model. The next obvious fix is to put a <strong>research</strong> phase in front of planning.</p><p>By the time we hit mid-2025, a third mode was appearing in serious agentic setups. A <em>research phase</em>: read-only, job is to understand the problem space, not to produce a solution. The artifact it creates is a description, not a prescription &#8212; a document that maps what is known before anyone decides what to do about it.</p><p>The intuition is right again: if the planning agent doesn&#8217;t know what it doesn&#8217;t know, it can&#8217;t plan well. Research is how it finds out. For the renewable energy report, thorough research might surface the fact that the intended audience is policymakers, not engineers &#8212; which changes the vocabulary, the technical depth, and the document&#8217;s opening frame.</p><p>Real progress, again. And again, not enough.</p><p>Watch the renewable energy report closely. The agent runs a solid research phase: downloads twenty recent papers, reads industry reports, synthesizes the state of the art on battery storage, hydrogen carriers, pumped hydro, and grid-scale thermal systems. Then it transitions into planning mode &#8212; in the same context window. And here is the problem: <em>the same context window</em>.</p><p>The planning agent isn&#8217;t a fresh mind looking at a research report. It&#8217;s the same agent, carrying everything it concluded during research, now deciding how to structure the work. If the research agent concluded &#8220;battery storage is the central challenge in the energy transition,&#8221; the planning agent will structure the report around battery storage. Not because it made a new decision &#8212; because it never had the opportunity to question the prior decision. It just kept going. The research was excellent. The plan followed naturally from the research. The report answered the question the research agent found most interesting. Not necessarily the question the reader needed answered.</p><p>The steelman still stands, and research sharpens it. The steelman says: &#8220;planning alone is enough, just review the plan.&#8221; Research proves it isn&#8217;t &#8212; but not because planning is the wrong approach. Research proves it because research without context isolation just moves the lock-in one step earlier. The same agent that researched now plans. It cannot escape its own prior conclusions, not because it&#8217;s incapable of abstract reasoning, but because those conclusions are literally sitting in its context, shaping every next token it generates.</p><p>A fresh planning agent, handed only the research artifact as a clean document, can genuinely question whether the research answered the right question. It can push back. It can say &#8220;your research focused heavily on battery chemistry, but I&#8217;m not sure that&#8217;s what the audience needs.&#8221; The same agent that did the research cannot do that. Not really.</p><p>But here is the real kicker: research gives you facts; it doesn&#8217;t tell you if you&#8217;re solving the right problem. Save that thought for a minute, we need one more step.</p><h2>Review After the Plan</h2><p>The <em>structure</em> is still the problem, not the model. And to fix it, we added a new, and in hindsight, pretty obvious step. A <strong>review</strong> phase after implementation.</p><p>This is a dedicated pass where a separate agent &#8212; or the same agent in a separate context &#8212; evaluates the produced artifact against known criteria. Not just &#8220;does this code run,&#8221; but &#8220;does this code do what we intended.&#8221; The distinction from implementation is real and it matters. An implementation agent is building; a review agent is hunting for the thing that will break it.</p><p>What review actually solves is real. The software auth implementation, evaluated by a review agent, surfaces real questions: Is the JWT expiry window set appropriately for the threat model? Is the bcrypt cost factor tuned for this hardware? Are session tokens actually invalidated on logout, not just expired? These are genuine bugs a fresh pass can find. I&#8217;ve seen review agents catch the kind of subtle mistake that a second human reader catches &#8212; not because they&#8217;re smarter, but because they&#8217;re looking for problems rather than building a solution.</p><p>But watch what happens when the review agent evaluates the software auth implementation against the plan.</p><p>The plan said: &#8220;implement JWT-based authentication for the web application.&#8221; The review agent confirms: JWT is implemented correctly. Bcrypt is used. Session management is in place. The implementation passes review. It ships.</p><p>First enterprise customer tries to log in: there is no tenant isolation. Every user in the system shares a single authentication namespace. The review agent found no bugs. The implementation had no bugs. The plan specified the wrong thing. And the review agent couldn&#8217;t catch it &#8212; not because it was careless, but because review only checks &#8220;did we implement the plan correctly.&#8221; Not &#8220;was the plan the right plan.&#8221; Those are different jobs. And you can&#8217;t review your way out of the wrong spec.</p><p>This isn&#8217;t a failure of intelligence. It&#8217;s a consequence of what the review agent is handed. It receives the implementation and the plan. It has no clean access to the original problem statement &#8212; what the user actually needed, what constraints were implicit in the product, whether the product served one customer or a hundred who shared a namespace. It&#8217;s evaluating the gap between the artifact and the plan, not between the artifact and the goal.</p><p>Human code reviewers fail the same way, by the way. Code review finds style violations, off-by-one errors, missing null checks. It rarely questions the architecture decision that was made three sprints ago and embedded in every layer of the codebase. That kind of question requires a different context &#8212; a different meeting, a design review, a fresh set of eyes on the spec rather than the implementation.</p><p>Review catches errors, but only inside the frame you committed to in the planning phase. You can&#8217;t review your way out of the wrong spec.</p><h2>Name the Problem Before Solving It</h2><p>Finally, the structure IS the <em>problem</em>, not the model.</p><p>Research doesn&#8217;t ask it. Planning doesn&#8217;t ask it. Implementation doesn&#8217;t ask it. Review doesn&#8217;t ask it. The question is: <em>what problem are we solving, and how will we know when we&#8217;ve solved it?</em></p><p>That&#8217;s not a rhetorical question. It has specific, concrete answers. And those answers should be a document &#8212; a specification &#8212; produced before the plan is written. Not during planning. Not as a side effect of research. Its own phase. Its own artifact.</p><p>A specification answers four questions, and it only takes one page to do it. What is the exact output we are trying to produce? What are the hard constraints it must satisfy? What does success look like &#8212; specifically, what would we check to confirm it? What does failure look like &#8212; what would make us say this didn&#8217;t work?</p><p>These sound obvious. They are almost never answered before a planning phase begins. In my experience, the reason is that they feel like they slow you down. They don&#8217;t. They prevent six months of work in the wrong direction. Think of it as a single page you could tape to the wall &#8212; the kind you&#8217;d point at during a disagreement about whether the output succeeded or failed.</p><p>Go back to the family move. After a thorough research phase &#8212; neighborhood data, school ratings, crime statistics, cost of living comparisons &#8212; a specification phase asks: what does a successful move look like for your family? The family sits with that question. It turns out they&#8217;ve never explicitly answered it.</p><p>The husband&#8217;s answer: proximity to his aging parents, who live in a specific region of the country. The wife&#8217;s answer: their daughter getting into a specific school district that has strong arts programs. The budget answer: keeping total housing costs under a threshold that lets them maintain their current savings rate. Three explicit success criteria.</p><p>The research phase found no conflicts because it was never told what it was optimizing for. The specification phase surfaces all three criteria before the plan commits to a single city. The planning phase can now do something useful: find cities that satisfy all three criteria &#8212; or, just as importantly, discover that no city satisfies all three and surface that conflict before anyone books a moving truck.</p><p>The software auth case is fast. Specification asks: what does correctly-implemented authentication look like for this product, given who its customers are? The answer: it must support multi-tenant isolation with strict data separation, SSO for enterprise customers, and a free tier with email-only login.</p><p>Now the plan can be written for the actual product. The research phase&#8217;s work on JWT and OAuth is still valid; it just needs to be read through the lens of multi-tenancy, which the specification made explicit.</p><p>The full chain, with its five concrete artifacts, looks like this.</p><p><strong>Research</strong> produces a collection of source materials plus a descriptive state-of-the-art report &#8212; what is known about this problem space. <strong>Specification</strong> produces a success-and-failure criteria document. <strong>Planning</strong> produces a concrete step-by-step plan &#8212; how to get from here to there, given the spec. <strong>Implementation</strong> produces an evaluable artifact &#8212; code, document, report, recommendation. <strong>Review</strong> produces an evaluation report checked against the specification, not just the plan &#8212; a real answer to &#8220;did we solve the problem?&#8221;</p><p>Five documents. Five <em>handoffs</em>. Five chances to catch the wrong frame before it becomes expensive.</p><h2>We Knew it all Along</h2><p>The fun part is all of this was known, at least in principle, since before 1987.</p><p>IDEO, a by-now ultra famous design consultancy, articulated a five-phase creative process that Stanford&#8217;s d.school later codified as <strong>Design Thinking</strong>.</p><p>Tim Brown formalized the diverge-converge logic in <em>Change by Design</em>. The five phases: <strong>Empathize</strong>, <strong>Define</strong>, <strong>Ideate</strong>, <strong>Prototype</strong>, <strong>Test</strong>. If those names sound familiar given what you&#8217;ve just read, that&#8217;s not a coincidence.</p><p>Empathize is research: go wide, gather context, talk to users, understand the problem space from the outside rather than the inside. Define is specification: converge on an explicit problem statement with clear success criteria, the &#8220;how might we&#8221; question that frames everything downstream. Ideate is planning: diverge again, generate candidate solutions, explore the space of possible approaches. Prototype is implementation: produce an evaluable artifact, something you can put in someone&#8217;s hands. Test is review: evaluate the prototype against the problem statement, not just against the prototype&#8217;s internal logic.</p><p>Every phase the agentic world has been bolting on since 2023 was already named, sequenced, and justified in a framework that predates the modern web by a decade and a half. It&#8217;s a framework every Silicon Valley startup, incubater, accelerator, and VS knows in and out. It is taught in bussiness majors all over the world. It&#8217;s literally the structure of most pitch decks. But it is still missing in most agentic protocols we use every single day.</p><p>The piece most clearly missing is the Define phase &#8212; the second one, which IDEO put second for a reason: without a clear problem statement, everything downstream answers the wrong question. It&#8217;s a very old insight the field keeps rediscovering from scratch &#8212; Agile&#8217;s Definition of Done, test-driven development&#8217;s failing-test-first, specification by example. Each was the same insight under a different name.</p><p>Now, here is the strongest version of your initial objection. &#8220;You don&#8217;t need all this structure. A great prompt specifies the audience, the format, the success criteria, the constraints. Write a better prompt and you get all five phases in one go.&#8221;</p><p>Let&#8217;s take this seriously, because it&#8217;s not wrong about prompt quality. A genuinely well-crafted prompt that specifies who the output is for, what format it should take, what it must accomplish, and what would make it fail &#8212; that prompt is effectively a specification. You&#8217;re right that prompt quality matters.</p><p>But a single prompt containing research context, goal specification, a plan, and execution instructions is not a cleaner version of the five-phase process. It&#8217;s five phases collapsed into one context window, with no mechanism for each phase to question the prior one&#8217;s conclusions.</p><p>When research and planning share a context, planning can&#8217;t interrogate research. When planning and implementation share a context, implementation can&#8217;t push back on the plan. When specification and review share a context, review is already biased toward confirming the specification it helped write. Prompt quality is about what you ask.</p><p>Phase independence is about who processes each answer, and whether they can genuinely disagree with the prior step. You can write the world&#8217;s best prompt and still hand it to an agent that will execute it inside the same narrowing tunnel, compounding the same assumptions with every step.</p><p>Picture someone reading their own manuscript for the fifth time &#8212; they no longer see what&#8217;s there, only what they meant to write. It is one of the most replicated findings in human psychology: once we form a belief, we interpret subsequent evidence through the lens of that belief. We notice confirming evidence, discount disconfirming evidence, and generate hypotheses that assume the belief is correct.</p><p>This is not a weakness of intelligence. Every mind &#8212; human or artificial &#8212; interprets through the conclusions it has already drawn. The agent that researched your problem already believes things about it. When it transitions to planning, it plans in service of those beliefs. The agent that planned has a solution in mind. When it implements, it makes countless micro-decisions that serve that solution. The agent that implemented defended choices as it worked. When it reviews, it reads its own output charitably.</p><p>Context isolation breaks this chain. A fresh context hasn&#8217;t seen the prior steps. It cannot be fooled by conclusions it never drew. It reads the artifact cold, which is the only way to genuinely evaluate it.</p><p>Design Thinking&#8217;s diverge-converge logic is not about <em>what</em> each phase does. It&#8217;s about <em>who</em> does it, and whether they can arrive at it without inheriting the prior phase&#8217;s commitments.</p><h2>Start Doing This Yourself Today</h2><p>The artisanal version of this is simpler than it sounds:</p><ul><li><p><strong>Treat each phase as a distinct conversation</strong>. Start a fresh session for each one.</p></li><li><p><strong>Hand it only the artifact from the prior phase</strong> &#8212; not the prior conversation, not your running context, not a summary of what you&#8217;ve been thinking about. The artifact alone.</p></li><li><p><strong>And tell the agent explicitly what mode it&#8217;s in</strong>. &#8220;You are in Research mode. Do not propose a plan. Do not suggest solutions. Your only job is to describe the problem space and produce a research report.&#8221;</p></li></ul><p>That instruction matters. Not because the model needs to be controlled, but because explicit mode assignment prevents the agent from sliding into execution behavior when it senses a gap to fill. Models are trained to be helpful; helpfulness steers every gap toward a solution. Naming the mode is how you resist it.</p><p>The discipline lives in you, not the tool. This works with any agent, any interface. Fresh context, explicit mode, artifact handoff. That&#8217;s the whole recipe.</p><p>If you want to go further, you can make phases structurally enforced rather than just instructed &#8212; agents that literally cannot execute, subagents that receive only the artifact, automated handoffs with no shared context. Programmable harnesses give you this level of control with permission levels per skill. (If you don&#8217;t have one, call me, <a href="https://apiad.github.io/aegis">I&#8217;ll lend you one for free</a>.)</p><p>One small step you can take today: add a specification phase to whatever workflow you already use. Before your planning phase writes a plan, ask for a success-criteria document first. One page. Explicit pass/fail conditions. What would make this output a success? What would make you throw it out? Review that document before planning begins.</p><p>This single addition &#8212; inserting a define phase between research and planning &#8212; catches more failures than adding a review phase after the fact. Because it catches them before the plan commits to the wrong goal.</p><p>What not to do: don&#8217;t implement all five phases as a mechanical checklist. And don&#8217;t add phases as ornamentation &#8212; a research phase that shares a context with planning adds conversation turns, not structure. More words in the same window is not more phases. The phases are context boundaries, not steps in a recipe. A phase that doesn&#8217;t produce a concrete artifact and doesn&#8217;t hand it to a fresh context adds nothing.</p><p>One document per phase. Fresh context per phase. That&#8217;s it.</p><h2>Structure Before You Re-Prompt</h2><p>Picture the artifact chain as a physical thing. A manila folder passed from one desk to the next. The research desk produces a report, closes it, slides it across. The specification desk opens only that folder, reads it, produces a criteria document, closes it, slides it across. The planning desk never opens the research folder &#8212; it opens only the criteria document.</p><p>And so on down the line. Each desk sees exactly one prior document. Each desk produces exactly one new document. The chain is what makes independence possible. You cannot hand off a vague intention. You cannot slide a feeling across a desk. Only a document.</p><p>Context isolation is the move most pipelines skip, and it&#8217;s the one that does the most work. Every phase that shares a context with a prior phase inherits its commitments. Not because the model is lazy or wrong &#8212; because that&#8217;s how cognition works, human or otherwise. We interpret through the lens of what we already concluded.</p><p>Context isolation is cheap: start a new session, pass only the artifact. The cognitive science is unambiguous: breaking the confirmation-bias chain requires a structural break, not a better instruction. Context isolation gets skipped because it looks optional. It isn&#8217;t.</p><p>Remember, the structure is the problem, not the model. Restructure before you re-prompt.</p><p>Until next time, stay curious.</p><div><hr></div><p><em>This is the core argument of the agentic workflows chapter in the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a> &#8212; the full chapter walks the failure cascade with more cases, the artifact design problem (a research report can be thorough and still hand the wrong thing forward), and the context isolation mechanics in depth.</em></p><p><em>This specific article is new<strong> </strong>content, still not in the book, but it will land there shortly. The book is 50% off while it&#8217;s in early access, and also <a href="https://books.apiad.net/books/mhai/">free to read online</a> in a custom reader I built: dark mode, font controls, progress tracking, offline support, the works. </em></p><p><em>If you want the architecture behind these systems &#8212; how they fail, what the harness around them should look like, and what to actually do about it &#8212; that is what the book is for.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/ai&quot;,&quot;text&quot;:&quot;Get Mostly Harmless AI - 50% off&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/ai"><span>Get Mostly Harmless AI - 50% off</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Mostly Harmless AI - Mid-week Report]]></title><link>https://blog.apiad.net/p/mostly-harmless-ai-mid-week-report</link><guid isPermaLink="false">https://blog.apiad.net/p/mostly-harmless-ai-mid-week-report</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Tue, 26 May 2026 10:31:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!YouB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YouB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YouB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!YouB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!YouB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!YouB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YouB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:839252,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.apiad.net/i/199222416?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YouB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!YouB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!YouB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!YouB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F252e6c69-1360-4057-bfab-88ab23d0e46e_1376x768.jpeg 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><p>This week I&#8217;m pushing towards a sane v2.0 version of <a href="https://store.apiad.net/l/ai">Mostly Harmless AI</a>, the book that shows you what AI is, what you can do with it, and where it can go wrong, without the hype or the bullshit. I mean &#8220;sane&#8221; because &#8220;done&#8221; is unreachable, this fields moves just too fast. So I&#8217;ll aim for something that is good enough for today&#8217;s reality, and revisit it in six months when we I have more to say&#8212;as I&#8217;m sure I will.</p><p>This is a quick recap of what&#8217;s changing from version 1.0 and how to get it. The first draft went out Friday. Since then I have been working through the rough edges, the kind you can only see once the whole book is in front of you at once. The version on the site today is materially better than what shipped on Friday.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/ai&quot;,&quot;text&quot;:&quot;Get Mostly Harmless AI - 50% off&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/ai"><span>Get Mostly Harmless AI - 50% off</span></a></p><h2>The two main changes since Friday</h2><p>The prologue is properly a prologue now.</p><p>When you open the prologue now, it begins where the idea began. The first edition opened in the 1950s, where the field opened. That was a mistake. The ideas that made modern AI possible were not born at Dartmouth in 1956 &#8212; they were assembled over three hundred years, by people who had no computers and no reason to suspect their work would one day converge into one. The new version begins with Leibniz in the 1670s, who imagined a <em>calculus ratiocinator</em> that could reduce human reasoning to calculation; with Ada Lovelace in the 1840s, who saw that symbols could represent anything, not just numbers; and with Alan Turing in 1936, who drew the boundary of what computation could do as a mathematical object, decades before the first computer existed. The rest of the prologue walks the seventy years between that theoretical foundation and the moment the field became an industry. The chronology now starts where the idea did, not where the field did.</p><p>There&#8217;s a glossary now.</p><p>The glossary for <em>Mostly Harmless AI</em> covers 484 entries &#8212; every person, system, technique, and field-of-art term used anywhere in the body. That includes Backpropagation, AlphaFold, the Bombe, ELIZA, RLHF, Stochastic Parrots, the Lighthill Report, and several hundred more. Each entry links bidirectionally: click a term in a chapter to jump to its definition; click a back-reference in the glossary to jump to where the term was used. When a term keeps recurring across chapters, the back-references show you the through-line. You can read the book without bringing any specific prior vocabulary.</p><p>I rewrote the preface in the same voice as the chapters &#8212; shorter, sharper, without the prompt-engineering rubble from the first AI-assisted cut. The cover is new too: a painterly collage running from Leibniz through Bletchley Park, ENIAC, Dartmouth, ELIZA, the multilayer perceptron, and the chatbot. Babbage was on the original cover and shouldn&#8217;t have been. He belongs in Lovelace&#8217;s chapter, and that&#8217;s where he is now.</p><p>Here is what the access model looks like and what your support funds, if you choose to give it.</p><h2>Access and support</h2><p><em>Mostly Harmless AI</em> is free to read in perpetuity at books.apiad.net. The reader there has clean typography, dark mode, and inline footnotes &#8212; no popups, no tracking, no required signup. Not a preview. The full book.</p><p>The PDF and EPUB are on Gumroad for those who prefer to read offline. Buying is a gesture of support, not a paywall &#8212; you get the same book either way. One-time purchase, every future edition in perpetuity, no subscription.</p><p>Part of the proceeds from the second edition funds the Spanish translation of <em>Mostly Harmless AI</em>.</p><p>If you can&#8217;t afford it, please take it free. I would rather you have the knowledge than the gesture. If the price is the only obstacle, write to me at <a href="mailto:apiad@apiad.net">apiad@apiad.net</a> and I will make it work.</p><h2>Where to find it</h2><p>The book is free to read at <a href="https://books.apiad.net/books/mhai">books.apiad.net</a>. The PDF and EPUB are at <a href="https://store.apiad.net/l/ai">Gumroad</a>. If you bought the first edition on Friday, your download already points at the latest files. A few things still to land by May 31 &#8212; if you find something off while reading, write to me. The next edition will be better for it.</p>]]></content:encoded></item><item><title><![CDATA[AI is doing something weird to Science]]></title><description><![CDATA[In both good and bad ways, and it won't go away.]]></description><link>https://blog.apiad.net/p/ai-is-doing-something-weird-to-science</link><guid isPermaLink="false">https://blog.apiad.net/p/ai-is-doing-something-weird-to-science</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Mon, 25 May 2026 12:40:28 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="4032" height="3024" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:3024,&quot;width&quot;:4032,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;a chalkboard with some writing on it&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&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="a chalkboard with some writing on it" title="a chalkboard with some writing on it" srcset="https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxOHx8c2NpZW5jZXxlbnwwfHx8fDE3Nzk1NjgyMzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 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><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@artturijalli">Artturi Jalli</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>Picture Donald Knuth. Eighty-eight years old, the man who wrote <em>The Art of Computer Programming</em> by hand in TeX, which he invented himself to be able to write his own books. The father of algorithmic analysis. The most laudeated living computer scientist. A legend, and a well-known AI skeptic. </p><p>Now picture him reading a printed chat log between a fellow colleague and <em>Claude Code</em>. Not skimming. Reading it in detail, because there is something genuinely baffling about it.</p><p>The log belongs to Filip Stappers, a mathematician who ran thirty-one coding explorations with Claude Opus 4.6, systematically probing a class of combinatorial objects Knuth had spent <em>decades</em> thinking about. Exploration 15 surfaced something unexpected: a structural pattern nobody had written down. </p><p>Knuth read it, judged it valid, proved it correct by hand, and wrote a paper about it. He called it &#8220;Claude&#8217;s Cycles.&#8221; Knuth noted, with his characteristic precision, that he&#8217;ll &#8220;have to revise his opinions about generative AI&#8221; one of these days.</p><p>Most accounts that open with this scene take one of two off-ramps.</p><p>The first off-ramp is the <strong>replacement narrative</strong>. AI is now the scientist. The model had the insight; Stappers just ran the prompts; Knuth read the output and judged it true. <em>Discovery has been automated</em>. We are, depending on your temperature, either liberated or obsolete.</p><p>The second off-ramp is the <strong>stochastic-parrot dismissal</strong>. It&#8217;s just a language model predicting tokens. It doesn&#8217;t understand combinatorics; it doesn&#8217;t understand anything. Stappers did the science; Claude shuffled plausible-sounding symbols. Attribute the discovery to the researcher, not the autocomplete.</p><p>Both off-ramps feel satisfying. Both are wrong. And they&#8217;re wrong in the same way: they&#8217;re answering the question &#8220;did AI do the science?&#8221; That&#8217;s the wrong question. The interesting object is not the agent. <em>It&#8217;s the loop.</em></p><p>The loop looks like this: a human poses a question; a model proposes candidates; a verifier filters the candidates; a human curates what survives. Round and round. What Stappers and Claude did is not fundamentally different in shape from what Tao and Lean are doing, or what the GNoME pipeline does in materials science, or what AlphaFold did for protein structure. The shape is the same. <em>The loop does the discovery.</em></p><p>I want to be clear about what that means, because it&#8217;s easy to hear it as either a compliment to AI or a dismissal. It is neither. It&#8217;s an empirical claim about where the causal action lives. Not in the model, not in the human, but in the <em>interaction structure</em> between them. Get that structure right and you get science. Get it wrong and you get confident nonsense at scale. The details of what &#8216;wrong&#8217; looks like are worth walking through carefully, and we&#8217;ll do that a couple of sections down.</p><p>But before we can dismiss either off-ramp, we need to walk four recent cases. Because in every one of them &#8212; Claude&#8217;s Cycles, Tao and Lean, AlphaFold, GNoME &#8212; the replacement narrative is not just philosophically confused. It is empirically wrong. The loop does the discovery.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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 The Computist Journal! 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><h2>Four Cases, One Shape</h2><p>In each of the following cases, the loop does the discovery. I&#8217;m going to give you four of them across four domains, because I think the pattern only becomes undeniable when you see it that many times. Same shape, different materials.</p><h3>Claude&#8217;s Cycles</h3><p>You already have the scene. Stappers runs thirty-one explorations. Not one inspired conversation, thirty-one numbered, documented, methodical probes. Claude proposes. Stappers evaluates. Knuth reads the surviving logs, verifies the mathematical claims by hand, and writes. One human author. One credited co-explorer. The question was Stappers&#8217;; the verification was Knuth&#8217;s; the curation of which thirty-one explorations were worth pursuing was human throughout. The model was the proposer.</p><p>That word matters, and I want to be precise about it. <em>Proposer.</em> Not discoverer, not author, not scientist. The one that generates candidates fast enough that the verifier can find something in the haystack.</p><h3>Tao and Lean</h3><p>Terence Tao, Fields Medal, the current standard of living mathematical genius, has been publicly working through what it looks like to use LLMs for mathematical research. His account in the <em>Notices of the American Mathematical Society</em> in 2025 is careful and specific, and I appreciate the care because it&#8217;s easy to overread these things in both directions.</p><p>Here&#8217;s what you&#8217;re actually looking at: an LLM proposes proof steps, intermediate claims, candidate lemmas, reformulations of the problem. Lean&#8217;s type-checker is the verifier. And Lean&#8217;s type-checker cannot be fooled. It either accepts a proof term or it doesn&#8217;t. There&#8217;s no &#8220;plausible-sounding but wrong&#8221; in Lean. The system rejects garbage instantly and silently. What the human does &#8212; Tao, or anyone in his lab &#8212; is curate: which of the surviving proof steps are worth following, which directions are worth pursuing. The loop is tight. The proposer is creative and unreliable; the verifier is reliable and uncreative; the human operates between them.</p><p>I&#8217;d reframe that: the breakthrough isn&#8217;t that LLMs can now do mathematics. It&#8217;s that the loop can now run fast enough and cheaply enough to be useful.</p><h3>AlphaFold</h3><p>I want to include this one even though it&#8217;s older and better-known, because it&#8217;s the case where people most confidently say &#8220;AI solved the problem.&#8221; And that confidence is telling.</p><p>The problem was fifty years old. How does a protein fold? You have the amino acid sequence; you want the three-dimensional structure; that structure determines function; that function determines everything downstream in biology and drug design. AlphaFold 2 proposes structures. Experimental crystallography verifies them: X-ray diffraction, cryo-electron microscopy, techniques that require real equipment, real samples, real physics. You cannot hallucinate a crystal structure into being; the protein either folds the way the model says or it doesn&#8217;t, and the experimental method tells you which. AlphaFold 3 extended this to molecular complexes: DNA, RNA, small-molecule ligands binding to proteins.</p><p>I think this is worth sitting with: human researchers curated which structures were worth solving, which proteins mattered, which findings were publishable. AlphaFold proposed. Nature verified. Humans curated.</p><h3>GNoME and A-Lab</h3><p>This is the one I find most clarifying, because the verifier here is the most brutally physical. You can&#8217;t argue with a crystal.</p><p>GNoME &#8212; Google DeepMind&#8217;s Graph Networks for Materials Exploration, if you want the full name &#8212; generated 380,000 candidate stable crystal structures. That&#8217;s the proposal stage. Then A-Lab, UC Berkeley&#8217;s autonomous laboratory, took 58 of those candidates and actually tried to synthesize them. Robotically. With real chemicals, real furnaces, real diffraction equipment to check what came out. Forty-one novel materials were successfully synthesized in 17 days.</p><p>Think about the verifier in that loop. Not a type-checker, not a scoring function. A physical robot in a real lab mixing real compounds at real temperatures and asking whether the crystal forms. You cannot fake a crystal. If A-Lab says it synthesized a novel stable material, it synthesized a novel stable material. The model proposed; the physical world verified; human researchers curated which structures were interesting enough to attempt.</p><p>I want to be blunt here: the pattern is not subtle. In every case: model proposes, independent verifier filters, human curates. The model is never the verifier. The model is never the question-poser. The model occupies exactly one slot in the loop, and it&#8217;s the slot that benefits most from creativity, speed, and a high tolerance for being wrong.</p><p>The replacement narrative requires you to believe the model is doing all three jobs. It isn&#8217;t. Empirically, demonstrably, across four domains, it isn&#8217;t.</p><p>Before we anatomize what changed inside the loop, it is worth noting that this shape is fifty years old. Because that changes what we should find surprising.</p><h2>The Loop Has Is Older Than You Think</h2><p>The loop does the discovery today in the same way it did the discovery in 1976.</p><p>I want to be somewhat insistent about this, because I think the historical amnesia around computational assistance in science is part of what makes AI discourse so unmoored. This isn&#8217;t new. What&#8217;s new is narrower than you think, and understanding exactly what changed is the only way to correctly evaluate what it means.</p><h3>Appel and Haken, 1976</h3><p>You probably know this one: the four-color theorem says any map can be colored with four colors such that no adjacent regions share a color. Mathematicians had been trying to prove it since 1852. In 1976, Kenneth Appel and Wolfgang Haken proved it. Using a computer. Their proof involved reducing the problem to 1,482 configurations and verifying each one computationally. No human read every step. No human could. The proof was real; the verification was mechanical.</p><p>The mathematical community was genuinely unsettled. The theorem wasn&#8217;t wrong. But the proof didn&#8217;t fit the usual epistemological frame. You couldn&#8217;t follow it the way you follow a conventional proof. You had to trust the computer. That discomfort was the first serious confrontation with what I&#8217;m calling the loop: humans posed the problem, humans designed the reduction, a computer verified the 1,482 cases, humans accepted the surviving result.</p><p>Same shape. Fifty years earlier.</p><h3>Hales and Flyspeck, 1998&#8211;2014</h3><p>Thomas Hales proved Kepler&#8217;s sphere-packing conjecture in 1998. The conjecture: the way you&#8217;d intuitively stack cannonballs, face-centered cubic packing, is in fact the densest possible arrangement. The proof relied on computer enumeration so extensive that the referees couldn&#8217;t verify it. They were &#8220;99% confident&#8221; and said so, which is an unusual thing for mathematical referees to say.</p><p>Sixteen years later &#8212; <em>sixteen years</em> &#8212; the Flyspeck project completed a formal verification of the proof in HOL Light, a proof assistant. I want to dwell on that: sixteen years. The loop had a very slow verifier in the middle. It still worked. The result was real.</p><p>Sixteen years from claim to verified closure. I find that humbling. We talk about AI accelerating science as though &#8220;fast&#8221; is a new property of loops. The loop has always done the discovery. Sometimes slowly.</p><h3>AI Feynman, 2020</h3><p>You won&#8217;t usually see this one in the AI-in-science timelines, but it belongs here. Silviu-Marian Udrescu and Max Tegmark built a system called AI Feynman that uses symbolic regression (searching the space of mathematical expressions) to recover physics equations from data. Feed it measured relationships between physical quantities; it proposes the equation. Tested on 100 equations from the Feynman Lectures on Physics. Human scientists posed the problems; the system proposed expressions; formal mathematical checks filtered them.</p><p>This is 2020, two years before the moment people usually date as the AI-in-science inflection. I find it clarifying: the loop is the same.</p><h3>What 2022 Actually Changed</h3><p>Here&#8217;s what changed. One thing in the loop changed, and it&#8217;s the proposer slot.</p><p>Before roughly 2022, the proposer in loops like these was domain-specific, narrow, and hand-engineered. AlphaFold&#8217;s architecture was designed from the ground up for protein structure prediction. The Flyspeck enumeration was written for Kepler&#8217;s problem. AI Feynman&#8217;s symbolic regression engine was built for recovering physics equations. The verifiers were already strong: formal proof checkers, physical experiments, crystallography. The curators were already human. But building a proposer required significant domain-specific engineering effort for each new application.</p><p>What changed, and I think this is the key move, is that <em>the proposer slot is now increasingly occupiable by general-purpose large language models that can be directed with natural-language specifications</em>. </p><p>The same model that helps Stappers explore combinatorial objects can, with different prompts, propose protein structures, generate proof steps, suggest material candidates. It&#8217;s not that the model does these things well in some absolute sense. It does them well enough that a strong verifier can find the real results in the output. And FunSearch, DeepMind&#8217;s system for mathematical discovery, goes a step further: it uses LLMs to generate the search strategy itself. AlphaEvolve extends this to evolving the algorithms. The proposer writes the proposer.</p><p>I want to flag what didn&#8217;t change: verifier reliability, human curation, question-posing. Knuth still read the logs. Tao still decides which lemmas are worth pursuing. The IMO committee still wrote the problems. The physical world still decides whether a crystal forms.</p><p>So: what exactly is the loop, and what changed inside it? The proposer and verifier are different jobs &#8212; you can confuse them, and that confusion is the mistake that both off-ramps make.</p><h2>Anatomy of the Loop</h2><p>The loop does the discovery. But what is the loop, exactly? It has four roles: <em>poser</em>, <em>proposer</em>, <em>verifier</em>, <em>curator</em>. They are not interchangeable.</p><p>I want to be specific about each, because vagueness here is where confused takes come from. When someone says &#8220;AI did the science,&#8221; they&#8217;re usually collapsing all four roles into one and attributing them to the model. When someone says &#8220;it&#8217;s just a tool,&#8221; they&#8217;re usually denying that the proposer role is meaningfully distinct from, say, a search engine. Both collapses are wrong.</p><h3>The Proposer Role</h3><p>I want to define this carefully, because the proposer role is where most confusion lives. The proposer generates candidates. That&#8217;s it. It doesn&#8217;t need to know which candidates are correct; it doesn&#8217;t need to understand the domain at the level required to verify; it doesn&#8217;t need to bear accountability for wrong answers. It just needs to produce output that the verifier can evaluate. Fast. In large quantities. With enough breadth that interesting things appear in the distribution.</p><p>This is a real and important job. Generating candidates well, in a domain, at the right level of specificity, with the kind of variety that makes the verifier&#8217;s job tractable, is genuinely hard. What LLMs are good at is producing plausible candidates in natural-language-specified domains. That&#8217;s useful precisely because &#8220;plausible&#8221; is a different bar from &#8220;correct.&#8221; The proposer buys lottery tickets; the verifier checks which ones won.</p><h3>Galactica, or: What Happens Without a Verifier</h3><p>In November 2022, Meta released Galactica, a large language model trained on scientific literature. I remember when this dropped: the pitch was that it could reason about science, write papers, explain concepts, generate hypotheses. It was fluent. Confident. It had read more papers than any human alive.</p><p>It was retracted in three days.</p><p>Galactica produced plausible-sounding but fabricated citations. It generated chemistry that was wrong. It stated incorrect facts with the same calm certainty it used for correct ones. The model had no verifier. It had no external check against which its proposals were filtered. It was a proposer talking directly to readers who were treating it as a verifier.</p><p>That&#8217;s the failure mode. &#8220;LLMs are bad at science&#8221; is the wrong lesson. Galactica was a capable proposer deployed without a loop. Dump the proposer&#8217;s output directly into the world, skip the verifier, and you get confident nonsense. The problem wasn&#8217;t the model. The problem was the missing loop.</p><h3>The Verifier Role</h3><p>I want to be careful here, because this is where the asymmetry lives. The verifier is not creative. The verifier must be <em>right</em>. These are the two properties that matter.</p><p>Lean&#8217;s type-checker does not hallucinate. It processes a proof term and either accepts it or rejects it, and its answer is correct by construction: it&#8217;s checking against the formal rules of type theory, and it doesn&#8217;t have opinions or moods or bad days. A crystal either forms or it doesn&#8217;t. X-ray diffraction either confirms the predicted structure or it doesn&#8217;t. These verifiers are not AI systems. They&#8217;re physics, or mathematics, or formal logic.</p><p>Here&#8217;s a slogan worth keeping: <em>the verifier is the one that matters.</em> A loop with a weak proposer and a strong verifier still produces valid science &#8212; it&#8217;s just slow, because it needs more proposals before one survives &#8212; this is just what &#8220;normal science has been so far&#8221;, slow proposer, strong verifier. </p><p>A loop with a strong proposer and a weak verifier produces Galactica. The asymmetry is important. You can have crap proposals and still win, as long as the verifier is robust. You cannot have a weak verifier and still win, no matter how impressive the proposer.</p><h3>Tao&#8217;s Insight on Composability</h3><p>Tao articulated this in his 2025 <em>Notices</em> piece, and I think it&#8217;s genuinely the right way to think about it: the value of the LLM-plus-formal-verifier combination comes from combining complementary weaknesses. LLMs are creative but unreliable. Lean is reliable but cannot be creative; it needs to be told what to check. Individually, each is limited. Together, the loop covers both limitations: the LLM proposes, Lean certifies, and you get results that are both novel and guaranteed correct.</p><p>This is not a new insight in the philosophy of science or even in computational science. It&#8217;s the same structure as Appel-Haken: creative human mathematicians posed and structured the problem, mechanical verification checked the cases. What&#8217;s new is that the LLM makes the creative-but-unreliable proposer slot much cheaper and more general to fill. You don&#8217;t need a domain expert to hand-engineer each proposer; you need a prompt and a general-purpose model.</p><h3>What&#8217;s Genuinely New Since 2022</h3><p>Let me be specific, because this matters for evaluating claims. Three things changed:</p><p>First, <strong>open-ended program synthesis</strong>. The proposer can now write code, not just fill templates. FunSearch and AlphaEvolve don&#8217;t just suggest candidate solutions; they generate the search strategy itself, which then searches for solutions. The proposer proposes how to propose. That&#8217;s a qualitative shift.</p><p>Second, <strong>cross-domain transfer at useful fidelity</strong>. The same general-purpose model can propose protein structure candidates, proof steps, crystal candidates, combinatorial patterns, all with natural-language specification. You don&#8217;t need to rebuild the proposer for each domain. The moat that used to come from hand-engineering a domain-specific proposer is largely gone.</p><p>Third, <strong>tight LLM-to-formal-verifier loop latency</strong>. The Flyspeck project took sixteen years. The Lean loop that Tao describes closes in seconds. When the loop runs fast, you can iterate fast, which means you can attempt more ambitious problems and accumulate signal faster about which directions are productive.</p><p>What hasn&#8217;t changed: humans pose the problems; the verifier is still not an LLM; which questions are worth asking remains entirely human. Knuth read the logs. Tao decides which lemma is promising. The IMO committee wrote the problems. The GNoME team decided that stable crystal structures were the target. The question of <em>what to look for</em> has not been automated.</p><p>With the anatomy clear, we can finally steelman both off-ramps seriously. And explain precisely why they are both wrong.</p><h2>Why Both Extremes Are Wrong</h2><p>The loop does the discovery, but what does that mean for who gets the credit?</p><p>I want to take both positions seriously, because I think each is tracking something real. Dismissing either one without engagement is how you end up with a position that sounds crisp but falls apart in the edge cases.</p><h3>The Maximalist Steelman</h3><p>Knuth is eighty-eight years old. He has spent decades thinking about the class of combinatorial objects Stappers was exploring. He hadn&#8217;t found the pattern that surfaced at exploration 15. Stappers, presumably, hadn&#8217;t either, or there wouldn&#8217;t have been thirty-one explorations looking for it.</p><p>Here&#8217;s the maximalist claim: if <strong>surprise</strong> is constitutive of discovery, if discovery is the moment something genuinely unexpected becomes known, then the model contributed something essential. It&#8217;s not that the model was incidentally involved in a process humans could have completed. It&#8217;s that the specific result didn&#8217;t exist in any human mind before the model generated it. A system that reliably surfaces what experts miss is doing something scientists do. The fact that the model can&#8217;t feel pride or bear responsibility doesn&#8217;t settle whether it participated in discovery.</p><p>The maximalist is pointing at something real. The model wasn&#8217;t executing a search strategy that a human had pre-specified. It was navigating a space in ways that produced genuinely unexpected output. That matters.</p><h3>The Dismissive Steelman</h3><p>A hammer drives nails. We don&#8217;t say the hammer built the house. Excel executes arithmetic the analyst specified; we don&#8217;t credit Excel with the financial model. AlphaFold was trained on structural biology data that human researchers collected over decades; the training objective was designed by human engineers; the evaluation criteria were set by human scientists. The model is a sophisticated tool. Sophisticated tools don&#8217;t discover; they execute.</p><p>The dismissive case is not purely rhetorical. There&#8217;s a serious accountability point underneath it: when a model-generated finding turns out to be wrong (and some will), who is responsible? If you&#8217;ve attributed agency to the model, you&#8217;ve obscured the accountability chain. The human who deployed the loop without a strong verifier, who curated poorly, who published without checking: that human is responsible. Diffusing credit into the model diffuses accountability too, and that&#8217;s a practical problem, not just a philosophical one.</p><p>The dismissive steelman is also tracking something real. The model didn&#8217;t choose the question. It didn&#8217;t decide the result mattered. It didn&#8217;t design the verifier. It didn&#8217;t stake its reputation on the finding.</p><h3>The Verdict</h3><p>Both steelmans are right about something; both are wrong about the structure.</p><p>The maximalist is right that the model contributes something essential, something that wouldn&#8217;t have been there without it. Wrong that this constitutes independent discovery: you cannot have independent discovery without the ability to pose a question, which requires caring about the answer, which requires the kind of intentionality the model doesn&#8217;t have.</p><p>The dismissive is right that the model doesn&#8217;t choose questions, doesn&#8217;t bear accountability, and shouldn&#8217;t be treated as an author in the full sense. Wrong that this makes it a mere tool in the hammer-and-nail sense. AlphaTensor, DeepMind&#8217;s system that found faster matrix multiplication algorithms, didn&#8217;t just execute a search the engineers specified. It found an algorithm that reconfigured what experts believed was achievable. That&#8217;s not executing; it&#8217;s navigating a combinatorial space in a way that produces genuine surprise. The hammer never surprises you &#8212; except when you&#8217;re not looking and it hits your finger, which by the way, AI can also do, and with a louder bang.</p><p>The right frame is, I think, the <strong>AI lab member</strong>. Indispensable, capable, sometimes surprising. Not a hammer; not a principal investigator, not a replacement for another human. Just a genuinely, qualitatively new kind of entity. An entity that occupies the proposer slot in the loop and does it better than anything that occupied that slot before, but doesn&#8217;t touch the verifier, doesn&#8217;t pose the questions, and doesn&#8217;t bear the accountability that comes with authorship.</p><p>If AI is a lab member and not a scientist, what does that do to the publishable paper, and to the metrics we use to measure science?</p><h2>But What Happens to the Paper </h2><p>The loop does the discovery. But the paper still needs to be written, and the paper is what the institution counts.</p><p>This is where I get more pessimistic, or at least more cautious. The loop is good news for science-the-activity. It&#8217;s more complicated news for science-the-institution.</p><h3>Discovery and Paper Count Were Already Decoupled</h3><p>Park, Leahey, and Funk published in <em>Nature</em> in 2023 a careful empirical study of scientific disruption, measuring across decades of papers and patents how often new work <em>displaces</em> the prior literature versus <em>consolidates</em> it. Their finding: disruption has been declining since 1945. Not linearly, not dramatically, but consistently. Meanwhile, paper count has been exploding.</p><p>The interpretation I find most compelling: the processes that generate papers and the processes that generate genuine advances were already decoupled before AI. A lot of papers are small increments, confirmations, replications, applications of known methods to new datasets. That&#8217;s not waste; science needs that infrastructure. But it means paper count was already a noisy proxy for discovery rate.</p><p>What an AI proposer does to this: it makes generating candidate findings cheaper. Cheaper to generate means more candidates, means more papers. Discovery and paper count decouple further.</p><h3>Goodhart as Accelerant</h3><p>Goodhart&#8217;s Law: when a measure becomes a target, it ceases to be a good measure. Academic institutions have been targeting paper count, citation count, journal impact for decades. Paper mills, factories producing fake or low-quality research for pay-to-publish journals, were already a pre-AI problem. Ioannidis documented the replication crisis in 2005 and again in 2018; most published findings in some fields don&#8217;t replicate, and that was before AI made generating plausible-sounding results cheaper.</p><p>The AI proposer is an accelerant deployed into a system already optimizing for the wrong thing. It makes the Goodhart problem worse. A lot worse. If you can generate a thousand candidate papers in the time it used to take to generate ten, and the verifier in your loop is peer review (slow, inconsistent, and famously gameable), then you have a problem that is structurally different from the pre-AI problem in scale.</p><p>I want to be clear: this is not an argument against AI-in-science. It&#8217;s an argument that the institution of science has a verifier problem, and AI proposers make that verifier problem more urgent. The fix is not to slow the proposer. The fix is to build better verifiers.</p><h3>The Optimistic Edge</h3><p>Here&#8217;s where I land, and I&#8217;m genuinely somewhat optimistic about this: if generating candidates is cheap, the scarce skill shifts upstream.</p><p>The researchers who will matter most in this environment are not the ones who can generate the most proposals. They&#8217;re the ones who can pose the right questions, who can identify which problems are worth solving before they know the answer, and the ones who can build strong verifiers. Question-selection and verifier-design become the competitive moat. That&#8217;s a real and important skill. It&#8217;s harder to fake than generating a paper. And it&#8217;s the skill that the loop most depends on.</p><p>If I&#8217;m hiring for a lab in 2026, I&#8217;m not looking for &#8220;AI scientist&#8221; as a job description. I&#8217;m looking for people who can look at a domain and say: <em>here is what a correct answer would have to look like, and here is how I would know if I found it.</em> That&#8217;s what the verifier is. Building it is hard. It requires deep domain knowledge, epistemological clarity, and the kind of judgment that comes from years of thinking carefully about what you&#8217;re actually trying to know.</p><h3>A Diagnostic Heuristic</h3><p>Let me give you something practical. When you read an AI-in-science result, a press release, a paper, a breathless tweet thread, ask this: <strong>what was the verifier, and who built it?</strong></p><p>If the verifier is Lean, or a crystal, or experimental replication with pre-registered protocols, or a physical measurement with known error bars: trust the result. The proposer&#8217;s reliability doesn&#8217;t matter much; the verifier caught the garbage. The finding is real regardless of how the candidates were generated.</p><p>If the verifier is not named, if the paper says &#8220;we used GPT-4 to generate hypotheses and evaluated them with GPT-4 to assess plausibility,&#8221; you&#8217;re looking at Galactica with extra steps. The proposer and the verifier are the same system. The loop is not closed. Be skeptical.</p><p>That diagnostic question is the institutional choice this moment is forcing. What was the verifier, and who built it? Let&#8217;s land it.</p><h2>The Verifier Is the One That Matters</h2><p>&#8220;I&#8217;ll have to revise my opinions about generative AI one of these days.&#8221;</p><p>Knuth said that. Not &#8220;AI did the science,&#8221; not &#8220;it&#8217;s just autocomplete.&#8221; Something more specific: <em>I may have underestimated what this thing can do in the right loop.</em> The loop did the discovery. The human owned the question and the verifier. The model was the proposer. And the result was real.</p><h3>The Investment Gap</h3><p>Here&#8217;s the uncomfortable institutional reality: almost all current investment is in the proposer. Larger models, more parameters, cheaper inference, better fine-tuning, faster generation. The race to make the proposer better is well-funded, well-publicized, and moving fast.</p><p>The verifier is comparatively neglected.</p><p>Formal verification tools like Lean exist, but they&#8217;re hard to use, require significant expertise, and don&#8217;t cover most scientific domains. Physical verification (A-Lab-style robotic synthesis) is expensive and slow relative to the speed at which the proposer can generate candidates. Experimental replication is underfunded as a scientific activity; it&#8217;s less prestigious than novel claims. The referee system in academic publishing was designed for a world where <em>generating</em> candidates was the hard part. It was not designed for a world where a model can generate ten thousand plausible candidates in an afternoon.</p><p>Hiring &#8220;AI scientists&#8221; misframes the institutional need. The need is for researchers who can pose hard questions and build reliable verifiers. The &#8220;AI lab member&#8221; frame points to what needs managing: not the model, but the loop. And the loop&#8217;s bottleneck right now is the verifier.</p><p>I should mention that this piece is drawn from the science chapter of <em><a href="https://store.apiad.net/l/ai">Mostly Harmless AI</a></em>, a book I&#8217;m writing about what AI actually does versus what the headlines claim. If you&#8217;re finding this useful, the book is where the longer argument lives.</p><h3>What Comes Next</h3><p>Next week: AI in creative output. Art, literature, music. Where the verifier question changes shape entirely. Because in mathematics and materials science, you can at least define what &#8220;correct&#8221; means. In creative domains, the verifier problem is not just harder. It&#8217;s constitutively different. What does it even mean for a creative proposal to be <em>correct</em>? I have some thoughts, and I think they&#8217;re going to make the science case look straightforward by comparison.</p><p>Stay curious.</p><div><hr></div><p><em>P.S. There&#8217;s a Subscribe button somewhere below this. I&#8217;m told it does something useful. My understanding of subscription mechanics is below the level of a confident stochastic parrot. I believe it works, but I haven&#8217;t checked every step. Click it anyway. The verifier here is whether you keep showing up next Monday.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Mostly Harmless AI - the book that explains you the AI without the bullshit]]></title><link>https://blog.apiad.net/p/mostly-harmless-ai-the-book-that</link><guid isPermaLink="false">https://blog.apiad.net/p/mostly-harmless-ai-the-book-that</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Fri, 22 May 2026 11:03:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Pubk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Pubk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Pubk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Pubk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Pubk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Pubk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Pubk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:845293,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.apiad.net/i/198725450?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Pubk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Pubk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Pubk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Pubk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F855f4d1b-58bf-4457-a8c0-507cb1e2fa77_1376x768.jpeg 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><p>The first draft of <em>Mostly Harmless AI</em>, second edition, is done. Large parts of it are new. The first edition shipped in August 2025, and I think it was good &#8212; honestly. But it was also a 2024 book living in a 2026 world: it predated the reasoning models, the agentic turn, and most of what happened to AI through 2025 and into 2026, which is a lot.</p><p>I read it again a couple of months ago and realized the map was wrong. So I rewrote it. Fortunately, most of the structural work survived &#8212; the chapters are in roughly the same places, the argument is the same argument &#8212; but the content is substantially updated, and the agentic AI material especially has gone from a footnote to a full chapter. The goal has not changed: a model strong enough to update on whatever comes next.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/ai&quot;,&quot;text&quot;:&quot;Get Mostly Harmless AI - 50% off&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/ai"><span>Get Mostly Harmless AI - 50% off</span></a></p><h2>What&#8217;s in it</h2><p>The book has three parts. It leans heavily into large language models and agentic AI because that is what most people encounter today when they encounter AI. But it starts from the ground up &#8212; from the symbolic systems of the 1950s &#8212; and the through-line is seventy years of the same argument running in the same field.</p><p><strong>Part I &#8212; Foundations</strong> explains how these things actually work, mechanism by mechanism. Six chapters: classical AI, machine learning, deep learning, language modeling, generative AI, agentic AI. The arc walks from symbolic reasoning to the agent loops running in 2026, and what the reader comes away with is the actual mechanism under the systems they talk to daily. <em>Attention</em>, <em>gradient descent</em>, <em>RLHF</em> &#8212; not the marketing line, the actual mechanism. If you have wondered what these terms mean, Part I is where you find out.</p><p><strong>Part II &#8212; Applications</strong> is about how to use these systems, and how the fields most people care about are using them. Seven chapters: a working-with-AI orientation, then knowledge work, scientific research, software development, education and learning, creative work, and policy and governance. Each chapter is dual-audience: an expert in that field gets meaningful good-practice and real gotchas; a non-expert &#8212; a policymaker reading the policy chapter, a parent reading the education chapter &#8212; comes away with a clear picture of how AI is reshaping that field. No code, no tutorials, no ten prompts to make ChatGPT love you. Just an honest account of what these systems do well, what they do badly, and what changes when you put one in front of someone trying to get real work done.</p><p><strong>Part III &#8212; Dangers</strong> is the hard part. Three chapters: alignment, the limits of language models, and the actual risks of AI. By <em>actual</em> I mean the harms that already exist in the world right now &#8212; deepfake fraud, autonomous-weapons concerns, biased decisions made at scale, workplace disruption &#8212; not the science-fiction extinction scenarios that collect most of the airtime. The existential-risk chapter is in there, but it sits inside a broader catalogue and gets the proportional treatment I believe it deserves. Part III is also where the book&#8217;s central position lands most clearly: the future is not predetermined. Neither doomer nor utopian framings are right. What we do about AI from here is a choice, and choices carry responsibilities.</p><p>Three parts. Sixteen chapters. Around two hundred and sixty pages of body prose. Almost three hundred footnote citations to peer-reviewed papers, technical reports, primary sources, news articles, and the occasional blog post that mattered. Every claim I could cite, I cited. Every claim I could not, I named as my own opinion. The full bibliography is at the back.</p><h2>Who it&#8217;s for</h2><p>Anyone who needs a working model of AI without necessarily writing code for it.</p><p>That is a wide audience, and I have tried to keep the prose accessible across all of it: knowledge workers thinking about how AI changes their jobs; educators thinking about how it changes their classrooms; policymakers thinking about how to regulate it without breaking it; parents thinking about what to tell their kids; entrepreneurs thinking about where the real opportunities are versus where the hype is; journalists, lawyers, doctors, executives, students of every stripe. The book assumes you are curious, reasonably literate, and not much else. The mathematics is very light if at all. The metaphors are tactile. The technical terms get defined the first time they appear.</p><p>Coders will find one chapter aimed directly at them &#8212; <em>AI for Software Development</em> in Part II &#8212; and they will find Part I interesting if they have not stopped to think about how the systems they use every day were trained. But this is not the book for coders. The book for coders is <em>How to Train your Chatbot</em>, the companion volume in the Computist Library. MHAI is the <em>what is happening and why does it matter</em> book; HTTYC is the <em>how do I build this stuff</em> book. Read <em>Mostly Harmless AI</em> first if you want the conceptual ground; jump to HTTYC if you are already comfortable with the concepts and want the engineering. Both are available in the <a href="https://apiad.gumroad.com/l/compendium">Computist Library bundle</a> if you want the pair at once.</p><p>That said &#8212; both books, all my books, everything I build &#8212; comes free to read online first. Which brings me to how this one works.</p><h2>Why I made it</h2><p>I have worked in AI for fifteen years and taught it for ten. I have been building AI-based tools for most of that time. What I have wanted to write for most of that period is a book that an intelligent reader &#8212; not an AI engineer, not a believer, not a doomer, just someone who needs to think clearly about what is happening &#8212; can read once and walk away with a model for what comes next. I did not have that book when ChatGPT launched. I did not have it when the agentic turn happened. I spent the last two years writing the version I wanted.</p><p>The short version of why that book matters: the AI conversation right now is dominated by two narratives that are both wrong. The first says AI will solve everything. The second says it will kill us. Most of the working reality is between them, and &#8220;between them&#8221; is not the same as &#8220;both extremes are partly right.&#8221; It is a third position, with its own shape and its own commitments. <em>Mostly Harmless AI</em> is my best attempt to lay that third position out clearly enough that you can argue with it.</p><p>Here is what the access model looks like, and what your support funds if you choose to give it.</p><h2>Access and support</h2><p><em>Mostly Harmless AI</em> is free to read in perpetuity. Not a preview &#8212; the full book, all sixteen chapters, from chapter one to the bibliography &#8212; at <a href="https://books.apiad.net/books/mhai/">books.apiad.net</a>, in a reader I built specifically for these books. Clean typography. Dark mode that does not hurt. Footnotes that surface inline. No tracking, no required signup. This is how all my books work &#8212; and my open source, and these posts. The knowledge comes first. The access model is not a funnel.</p><p>Yes, asking you to pay for something that is also free is a slightly awkward pitch &#8212; I know. But here is the honest framing: the PDF and EPUB are on <a href="https://apiad.gumroad.com/l/ai">Gumroad</a> for those who prefer to read offline or want to own a copy. Buying it is a gesture of support, not a paywall. The work is not free to make. The research, the rewriting, the next book &#8212; that is what your support actually funds.</p><p>Part of the proceeds from the second edition funds the Spanish translation of <em>Mostly Harmless AI</em>. That is a real thing I am working toward, and your purchase moves it forward.</p><p>And if you genuinely cannot pay: please, take it free. I would rather you have the knowledge than the gesture.</p><h2>Where to find it now</h2><p>The draft is at <a href="https://books.apiad.net/books/mhai/">books.apiad.net</a> right now, mostly readable as-is. The structure is settled, the prose is in place, and most chapters have been through at least one editing pass. The next two weeks are the final polishing phase &#8212; a full read for consistency, a copy edit for the sentences I have not caught yet, a few diagrams I want to redraw. The finished book ships on May 31. Gumroad for the PDF and EPUB, books.apiad.net for the free reader, both at once.</p><p>If you find something off while reading the draft, write to me. The May 31 edition will be better for it &#8212; and yes, I am aware of the mild absurdity of writing a book about AI while using AI to help write it. The ideas are all there. The 31st is just the cleaner version.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/ai&quot;,&quot;text&quot;:&quot;Get Mostly Harmless AI - 50% off&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/ai"><span>Get Mostly Harmless AI - 50% off</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[The 80% AI Reliability Horizon]]></title><description><![CDATA[The real number every AI engineer should be tracking]]></description><link>https://blog.apiad.net/p/the-80-ai-reliability-horizon</link><guid isPermaLink="false">https://blog.apiad.net/p/the-80-ai-reliability-horizon</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Thu, 21 May 2026 14:28:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!jhaA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jhaA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jhaA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jhaA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jhaA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jhaA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jhaA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:674811,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.apiad.net/i/198700078?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jhaA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jhaA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jhaA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jhaA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F791d9975-e296-4f78-a682-e797785b86e8_1376x768.jpeg 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><figcaption class="image-caption"><em>Adapted from Friedrich, <a href="https://en.wikipedia.org/wiki/Wanderer_above_the_Sea_of_Fog">&#8220;Wanderer above the Sea of Fog&#8221;</a> (c. 1818), Kunsthalle Hamburg &#8212; the horizon you can see is not the horizon you get to stand on. Rendered with Nano Banana 3 via <a href="https://github.com/apiad/mosaico">mosaico</a>.</em></figcaption></figure></div><blockquote><p><em>Every post on the blog this month is on the theme of agent reliability, anchored on the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a> &#8212; 50% off during early access &#8212; where the Limitations chapter walks all seven failure modes that compound into the curve below. You can also <a href="https://books.apiad.net/books/mhai/">read the whole book online for free</a>. More at the end.</em></p></blockquote><p>The headline number you&#8217;ve seen on every AI-progress chart &#8212; &#8220;model X completes two-hour tasks half the time&#8221; &#8212; is the <strong>50% reliability horizon</strong>. That number is moving fast. Doubling every seven months, per METR&#8217;s time-horizon work. It&#8217;s the curve on every AI-progress chart, the one conference talks lean on, the one that lands in investor decks.</p><p>The number that decides whether you can actually deploy an agent is a different one.</p><p>The <strong>80% reliability horizon</strong> &#8212; the task length at which an agent finishes well enough that you would not feel the need to check &#8212; sits 70&#8211;80% below the 50% figure, and it moves up far more slowly. That gap is the difference between demo and deploy. The 50% is what passes the eval. The 80% is what survives the afternoon you weren&#8217;t watching. Not two hours. Thirty minutes you&#8217;d hand off.</p><p>I want to be precise about what I&#8217;m not arguing. I&#8217;m not arguing agents are broken or that AI progress isn&#8217;t real. It is real, and it&#8217;s fast. I&#8217;m making the narrower claim that the 50% and 80% horizons move at different speeds, and that the 80% is the one that matters when someone else&#8217;s data is on the line. This post is the math behind the gap, why it&#8217;s structural, and what you can do about it.</p><p>If you&#8217;re building on agents, you are building on the 80% horizon. The 50% number is for the marketing deck.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>Two horizons, two stories</h2><p>METR&#8217;s methodology is clean: take a population of tasks with measured human completion times, then find the longest task a given model clears at success rate X. Do that for X = 50%, and you get the 50% horizon. Do it for X = 80%, and you get a different curve. A different story.</p><p>The 50% horizon has been doubling roughly every seven months. Late 2025, it sat around a couple of hours for software tasks. That&#8217;s the curve that makes headlines. That&#8217;s the curve you&#8217;ve seen on every slide. Striking.</p><p>The 80% horizon sits roughly 70&#8211;80% below. The same agent that clears a two-hour task half the time clears a half-hour task four-times-in-five. Not two hours. Thirty minutes. And that gap doesn&#8217;t close at the same rate. It moves slowly, stubbornly, for reasons that are mathematical before they are engineering.</p><p>So you have two curves growing at different speeds. The 50% horizon is the curve of capability: what can this system do, under ideal conditions, at least sometimes. The 80% horizon is the curve of trust: what can this system do reliably enough that you&#8217;d hand it a production key and walk away.</p><p>They are not the same curve. And they do not close the same way.</p><p>The longer your task horizon, the wider the gap between can-do-it-sometimes and can-be-trusted-with-it. The mechanical reason is one piece of math.</p><h2>Probability arithmetic</h2><p>Here&#8217;s the setup. A language model you&#8217;re calling spends a fixed compute budget per output token. Each step in a multi-step process has some per-step success probability <em>p</em> that is strictly less than one. The model is stochastic, the world is noisy, context degrades.</p><p>String <em>n</em> steps together, and the probability that all of them succeed is roughly <em>p<sup>n</sup></em>. That&#8217;s it. That&#8217;s the math.</p><p>Here&#8217;s what that looks like with actual numbers. Suppose your agent is excellent: <em>p</em> = 0.99 per step. That&#8217;s a 99% success rate on any single action. Compound it over 100 steps: 0.99<sup>100</sup> &#8776; 0.37. You&#8217;ve gone from near-certain to worse-than-a-coin-flip without anyone making an error. Now drop to <em>p</em> = 0.95 (still quite good, still 95% per step). Over 100 steps: 0.95<sup>100</sup> &#8776; 0.006. Six in a thousand runs succeed.</p><p>This is not a gap you close with next year&#8217;s training run. It is the shape of any probabilistic process operating in sequence over time. The curve doesn&#8217;t change when you improve <em>p</em>; it just shifts outward.</p><p>Reasoning models &#8212; the o-series, R1, extended-thinking variants &#8212; are valuable here. They buy you a higher per-step <em>p</em>, and they let you spend more steps at that higher rate. Both matter. But they push the curve outward. They do not change its shape.</p><p>Two pieces of evidence you should hold next to that math. <em>GSM-Symbolic</em> (Apple, 2024): perturb a math problem the model has seen (swap a name, change a number) and accuracy collapses even when the reasoning path is identical. The model has memorized the route, not the reasoning. <em>Faith and Fate</em> (Microsoft, 2023): transformer accuracy degrades with computational-graph depth even when each individual sub-step is solvable in isolation. Depth itself is the failure axis. More steps means more surface for <em>p</em> &lt; 1 to accumulate.</p><p>Reasoning models buy you a higher per-step <em>p</em> and more steps to spend. They do not change the shape of the curve.</p><h2>Where the chain gets long</h2><p>Agents are exactly the setup that makes <em>p<sup>n</sup></em> painful.</p><p>Think through a typical agent run: read prompt, plan, call tool, read result, call tool again, critique output, adjust plan, call final tool, write response. Seven steps if you&#8217;re being generous. A real production agent reaches hundreds. Each step is one more <em>p</em> rolled. Each tool call is one more chance the orchestrator hands the tool the wrong arguments &#8212; garbage in, deduction out.</p><p>Self-critique doesn&#8217;t repair this &#8212; and you can verify the result yourself if you&#8217;ve tried it. Huang and colleagues (2024) showed that intrinsic self-correction without an external oracle signal actually degrades performance. The model talks itself out of correct answers as often as it talks itself in. The paradox is clean: if the model could recognize the error, it would not have made it. Asking it to introspect on failures is asking the broken compass to check itself.</p><p>So let&#8217;s put numbers on a real scenario. An agent that succeeds on each of five steps 95% of the time lands at 0.95<sup>5</sup> &#8776; 0.77. Decent. Not great, but workable. Now extend that same agent to a fifty-step trajectory: 0.95<sup>50</sup> &#8776; 0.08. Eight runs out of a hundred finish correctly.</p><p>The demo ran five steps. The deploy runs fifty. The demo and the deploy are two different machines.</p><p>That&#8217;s the 80% horizon you&#8217;ll actually feel in production. It&#8217;s not a philosophical concern about AI reliability in the abstract. It&#8217;s the arithmetic of what happens when you take a stochastic generator and ask it to maintain a chain of reasoning over a long enough trajectory that <em>p<sup>n</sup></em> has time to do its work.</p><h2>What you can actually do</h2><p>Three mitigations. Each one genuine, and each one with a ceiling you should know before you commit.</p><p><strong>Verifier-shaped tasks.</strong> Where the output can be checked deterministically (arithmetic, code that compiles and runs, SQL that parses, a formal proof), you can recover trust that the probabilistic generator alone cannot provide. A SAT solver beats an LLM on deductive closure every time. The architecture that wins here is LLM-proposes-candidate, deterministic-system-signs-off. The generator explores the space; the verifier approves the exit. This is, incidentally, the same pattern Monday&#8217;s post on the seventy-year argument named: a deterministic shell around a stochastic core, applied at the task level rather than the system level. The twist is that not every task has a fast verifier. Code that runs is checkable; code that runs <em>correctly for all future inputs</em> is not.</p><p><strong>Retrieval-augmented generation.</strong> If the fact your agent needs is no longer arbitrary recall but lives in a curated document the model is required to cite, then Kalai and Vempala&#8217;s 2024 lower bound on calibrated hallucination does not apply to that fact. Most agent failures upstream of a tool call are recall failures the agent doesn&#8217;t know it&#8217;s making; retrieval changes the error mode from confident confabulation to visible gap. RAG turns a free-running generator into a paraphrase-and-summarize system over a known corpus. The reach of the system is now bounded by the reach of the index. But anything outside that index is back to pure <em>p</em> &lt; 1 territory.</p><p><strong>Narrow the horizon.</strong> The cheapest move is the one nobody wants to make: don&#8217;t deploy your agent on a fifty-step trajectory. Cut it to five. Hand off to a human at the boundary. At five steps with <em>p</em> = 0.95 you&#8217;re at 0.77; at fifty steps you&#8217;re at 0.08. That&#8217;s not a small difference. That&#8217;s the difference between a tool that works and a demo that occasionally works. Now, this trades <em>autonomy</em> for <em>reliability</em>. That trade is worth making in most production contexts right now. Whether it&#8217;s worth making in your context is a product question, not a research question.</p><h2>Watching the right number</h2><p>The 50% number will keep doubling and you should track it. That is real progress and worth watching closely.</p><p>But it is not the number your users feel. The number your users feel is whether the agent finished their task well enough that they didn&#8217;t have to re-run it, check its work, or clean up after it. The difference between &#8220;I tried that AI agent thing and it was magic&#8221; and &#8220;I tried that AI agent thing and it broke my Friday&#8221; is roughly the distance between the 50% horizon and the 80% horizon at your task length.</p><p>The shape of the next several years of agent engineering is already visible in the mitigations you&#8217;ll be reaching for: deterministic verifiers around stochastic generators, retrieval around recall, short trajectories with human handoffs where the math demands it. Not because agents are weak. They&#8217;re remarkable. But the <em>p<sup>n</sup></em> curve doesn&#8217;t care about benchmark scores. It cares about chain length.</p><p>One number, slowly creeping upward, every quarter. Watch that one.</p><p>Until next time, <strong>stay curious</strong>.</p><div><hr></div><p><em>If the 80%-horizon framing landed, the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a> walks the seven failure modes that produce the curve &#8212; the calibrated-hallucination lower bound, the U-shaped attention curve, the reversal curse, the depth ceiling on deduction, the rest. <strong>50% off during early access.</strong> You can also <a href="https://books.apiad.net/books/mhai/">read the whole thing online for free</a> in a custom reader I built &#8212; dark mode, font controls, offline support, the works.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/mhai&quot;,&quot;text&quot;:&quot;Get Mostly Harmless AI - 50% off&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/mhai"><span>Get Mostly Harmless AI - 50% off</span></a></p><p><em>And if you want everything I&#8217;ve written, plus everything I&#8217;m going to write, that&#8217;s <a href="https://apiad.gumroad.com/l/compendium">the Compendium</a>. One purchase, in perpetuity.</em></p>]]></content:encoded></item><item><title><![CDATA[It's Tokens all the Way Down]]></title><description><![CDATA[How language models understand image, audio, and video.]]></description><link>https://blog.apiad.net/p/its-tokens-all-the-way-down</link><guid isPermaLink="false">https://blog.apiad.net/p/its-tokens-all-the-way-down</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Tue, 19 May 2026 10:26:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Gnl8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Gnl8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Gnl8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!Gnl8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!Gnl8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!Gnl8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Gnl8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1907923,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.apiad.net/i/198385396?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.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_!Gnl8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!Gnl8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!Gnl8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!Gnl8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F213e8caa-6892-4379-a3da-13ce32828faa_1376x768.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><p></p><blockquote><p><em>Part of the run-up to the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a> &#8212; 50% off during early access &#8212; where this is the spine of a new chapter on generative and multimodal AI. You can also <a href="https://books.apiad.net/books/mhai/">read the whole book online for free</a>. More at the end.</em></p></blockquote><p>One morning, not so long ago, perhaps you asked Claude (or Gemini, or ChatGPT) to do something for you, and decided it was easier to just give it a picture of it than explain the whole thing. Perhaps it was &#8220;how do I cook this thing?&#8221; or &#8220;what building is that?&#8221;  or &#8220;do this homework for me, please, please, my live depends on it&#8221;.  Then you uploaded the picture, and back came a textual response.</p><p>Not happy with what the bot understood, you decided a thorough explanation was owed. But, alas, since all we got is a couple fatty fingers for typing, you decided it was best if you explained it with your own voice. And again, uhms and ehms notwithstanding, you again got a full response back, this time with an audio voice over. </p><p>Ten years ago, this simple dance of back-and-forth multimodal information would have required four separate research fields, each with its own conferences, its own vocabulary, and its own priesthood. They have quietly become one single thing. It&#8217;s all tokens all the way down. Language has subsumed all modalities. This is how.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>The recipe never cared what it was eating</h2><p>Strip &#8220;generative AI&#8221; down to the one idea doing all the work and you get a single sentence: look at a big pile of examples, learn the distribution that produced them, then draw new samples from it. That is the whole trick. It is what a language model does, and it is the only thing a language model does. It is also what an image model does, and an audio model, and a video model.</p><p>The recipe is indifferent to what the examples are. Text is a one-dimensional run of symbols. An image is a two-dimensional field of colour. Audio is a pressure wave sampled tens of thousands of times a second. Video is all of that, plus time, which is why it is the hardest. Four different shapes of data, one identical question asked of each: <em>given what I have seen so far, what plausibly comes next?</em> The machinery that answers that question does not need to know whether &#8220;next&#8221; means a word, a patch of pixels, or a slice of waveform. It only needs the data turned into a sequence of countable things. </p><p><em>Tokens.</em></p><p>So the thing we have been calling a language model was never really about language. Or, put better, it was never about <em>written </em>language. It turns out, language is something far more powerful. </p><p>Ask any linguist, and they&#8217;ll say any set of sequences of distinct symbols (<em>tokens</em>) can be modelled as a language. It doesn&#8217;t matter what your symbols are&#8212;letters, words, patches of images, numbers in a math formula, whatever&#8212;language is just the structure around them, what makes some sequences valid and others nonsense. </p><p>This is the key idea. All else is (incredibly good) engineering.</p><h2>A decade of building the same machine, separately</h2><p>It did not look that way while it was happening. For about a decade every modality got its own bespoke contraption, and each one looked like its own discipline.</p><p>Image people had <strong>generative adversarial networks</strong>: a forger and a detective locked in a training duel, the forger getting better at faking until the detective could no longer tell. The beautiful idea buried in there &#8212; and the one that survived the technique itself &#8212; was the <em>latent space</em>: a compressed interior map of &#8220;all possible faces,&#8221; where walking in a straight line morphs one plausible face smoothly into another. GANs were temperamental, prone to collapsing into a single good fake and refusing to leave, and by the early 2020s they had lost the lead. The latent-space intuition outlived them and runs underneath everything that came after.</p><p>Then <strong>diffusion</strong> took over image generation with a trick that sounds like it shouldn&#8217;t work. Take a real photo, add a little static, add a little more, keep going until it is pure snow. Now train a network to undo one step of that. To make a new image, start from snow and run the undo, over and over, until something coherent surfaces. It is sculpture by removing noise instead of removing marble, and it is what powers essentially every image generator you have used.</p><p>Audio had its own separate lineage: speech-to-text built one way, text-to-speech another, music a third. Text had the large language models, off in their own enormous-budget corner of the field. Four communities, four sets of architectures, four sets of war stories. If you had asked, in 2021, whether the image people and the language people were building the same machine, both sides would have laughed.</p><h2>CLIP quietly knocks out the wall</h2><p>The crack in the wall came from a 2021 model whose job sounds almost too modest to matter: teach one system that the word <em>dog</em> and a photograph of a dog are talking about the same thing.</p><p>The way you do that is to train a text encoder and an image encoder <em>together</em>, on hundreds of millions of caption-and-picture pairs, with one instruction: put a picture and its true caption close together in a shared space, and shove mismatched pairs apart. What you get at the end is a single space where &#8220;a photo of a golden retriever&#8221; and an actual photo of a golden retriever land as neighbours. Text and pixels, in the same room, with the same coordinates.</p><p>That sounds like a party trick for image search. It was the hinge the whole field turned on. Once text and images live in one space, text can <em>steer</em> image generation &#8212; point the diffusion process at the region of the space that means &#8220;golden retriever in a spacesuit,&#8221; and let it denoise toward there. Every text-to-image system you have used is, under the paint, that move. And the deeper implication was harder to ignore than the application: if you can put two modalities in one space, the wall between them was never structural. It was just a wall nobody had walked through yet.</p><h2>Tokens all the way down</h2><p>Here is where it lands. By the mid-2020s the bespoke machines stopped being separate machines.</p><p>The move is almost embarrassingly direct. Tokenise everything. Text already broke into tokens. Cut an image into a grid of patches and treat each patch as a token. Run audio through a neural codec that emits discrete chunks, and those are tokens too. Now you do not have a text stream and an image stream and an audio stream. You have one stream of tokens that happen to have come from different alphabets, and you train a single model on the only objective that was ever in play: predict the next token, whatever kind it is.</p><p>A model trained that way reads and writes everything, because to it there is no &#8220;everything&#8221; &#8212; there is just the sequence and the next position in it. You have used these. The one that holds a spoken conversation with sub-second latency, looks at the photo you paste in, and writes you a paragraph back is not a language model bolted to an image model bolted to a speech model. It is one model that was never told these were different problems.</p><p>Which is why the question that organised the field for a decade &#8212; <em>is this a language model or an image model?</em> &#8212; has quietly stopped having an answer. It is the same machine. The only thing that ever changed between text and pixels and sound was the alphabet, and the transformer emitting the next token has never cared which alphabet it is spelling in. It is tokens all the way down. &#8220;Language modelling&#8221; was a local name for something with no allegiance to language at all: modelling sequences of anything we can count.</p><h2>The honest part</h2><p>It would be easy to end on the astonishment, and the astonishment is real. One model, every modality, falling out of one stubbornly simple objective applied to a wider and wider definition of &#8220;token&#8221; &#8212; that is one of the genuinely beautiful results of the decade, and the kind of unification that does not come along often.</p><p>But unification is not the same as understanding, and I am not going to let the elegance smuggle that past you. A system that can place &#8220;dog&#8221; next to a dog in its latent space has learned the statistics of how dogs are described and depicted. </p><p>Whether it has learned what a dog <em>is</em> is a different question, and the convergence story does not answer it. It just makes the question apply to every modality at once instead of only to text. The machine got more general. It did not get more grounded necessarily. Both of those are true at once, and the interesting work of the next few years lives in the gap between them.</p><p>Until next time, stay curious.</p><div><hr></div><p><em>This is the core argument of a new chapter in the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a> &#8212; the full chapter walks GANs, diffusion, CLIP, audio, and native multimodality with the scenes and citations this post had to cut, and it is 50% off during early access. The whole book is also <a href="https://books.apiad.net/books/mhai/">free to read online</a>. If you want the rest of the argument &#8212; how these systems are trained, where they break, and what to actually do about it &#8212; that is what the book is for.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/mhai/fiftyoff&quot;,&quot;text&quot;:&quot;Get it (50% off) for life&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/mhai/fiftyoff"><span>Get it (50% off) for life</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[70 Years of AI History in 10 Minutes]]></title><description><![CDATA[A summary to the updated zero-th chapter of Mostly Harmless AI v2.]]></description><link>https://blog.apiad.net/p/70-years-of-ai-history-in-10-minutes</link><guid isPermaLink="false">https://blog.apiad.net/p/70-years-of-ai-history-in-10-minutes</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Mon, 18 May 2026 11:40:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!uPIg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uPIg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uPIg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg 424w, https://substackcdn.com/image/fetch/$s_!uPIg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg 848w, https://substackcdn.com/image/fetch/$s_!uPIg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!uPIg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uPIg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg" width="1456" height="1130" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1130,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2468838,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.apiad.net/i/198246760?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uPIg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg 424w, https://substackcdn.com/image/fetch/$s_!uPIg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg 848w, https://substackcdn.com/image/fetch/$s_!uPIg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!uPIg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11712488-c3b8-451c-903d-be69a0286d5f_3820x2964.jpeg 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><figcaption class="image-caption"><em>Raphael, <a href="https://en.wikipedia.org/wiki/The_School_of_Athens">&#8220;The School of Athens&#8221;</a> (1509&#8211;1511), Apostolic Palace &#8212; Plato points up to the eternal forms (the rule-followers); Aristotle&#8217;s palm presses down to the empirical world (the pattern-finders).</em></figcaption></figure></div><blockquote><p><em>Every post on the blog this month is on the theme of agent reliability, anchored on the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a> &#8212; 50% off during early access &#8212; where the history below is the full ~8,000-word opening chapter, with 70+ references and all the scenes this post had to cut. You can also <a href="https://books.apiad.net/books/mhai/">read the whole book online for free</a>. More at the end.</em></p></blockquote><p>Seventy years ago, two men sat in two different rooms and disagreed about what a thinking machine should look like. Neither of them has been proven right. Both have been proven half-right, several times, in alternation, for the whole of my lifetime and most of yours.</p><p>I think the entire history of AI is that one argument, still going.</p><p>The first camp wanted to build minds out of rules. Feed the machine enough knowledge in a logical enough form, and reasoning falls out of the logic. It called itself a lot of things over the decades &#8212; symbolic AI, knowledge-based systems, good old-fashioned AI &#8212; but its home is <strong>rationalism</strong>. The second camp wanted to build minds out of examples. Feed the machine enough data, in any messy form whatsoever, and behavior falls out of the statistics. It also kept renaming itself &#8212; connectionism, machine learning, deep learning &#8212; but its home is <strong>empiricism</strong>. Same goal, a machine that does what intelligent people do. Sixty years of disagreement about how.</p><p>Here&#8217;s the ending, spoiled early, because this isn&#8217;t a thriller. The argument did not produce a winner. It produced a marriage. The chatbots, the image generators, the agents writing code while you sleep &#8212; none of them is one side beating the other. They&#8217;re both sides, finally forced to share a workshop. Let me walk you through how we got there. Fast.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>Both seeds, one summer</h2><p>They were planted within five years of each other. In 1943, McCulloch and Pitts wrote down a neuron as a weighted sum with a threshold &#8212; twelve pages, the seed of the empiricist branch. In 1950, Turing refused to define <em>thinking</em> and proposed a behavioral test instead, a question both camps could chase. In 1956, ten people spent a summer at Dartmouth, coined the phrase <em>artificial intelligence</em>, and planned to crack language and reasoning in a few months. (We are still working on it.) In 1957, Rosenblatt built the <strong>Perceptron</strong>, the first machine that learned from examples, and the <em>New York Times</em> announced it would soon walk, talk, and be conscious of its own existence.</p><p>Two foundational myths, in the ground, in the same decade. The rest is which one got watered.</p><h2>The rationalists win the first round</h2><p>And they win it convincingly. In the 1950s and 60s compute is tiny and data, in the sense of millions of labeled examples, does not exist. </p><p>What you <em>can</em> do is write a program that does something specific and inspect every step of it. So the symbolic camp gets the better results and the better tools. Newell and Simon&#8217;s theorem-prover. McCarthy&#8217;s LISP. Weizenbaum&#8217;s ELIZA &#8212; four pages of pattern-matching that understood nothing, and that people confided in anyway. (Hold onto ELIZA. The field will relearn that exact lesson about six more times.) Winograd&#8217;s SHRDLU, fluent and thoughtful inside a closed world of colored blocks.</p><p>The catch was always the world. SHRDLU&#8217;s blocks could all be known, listed, reversed. The real world has rain, and grandparents, and the smell of coffee, and you cannot list it. In the closed world of symbols, symbols were enough. The next decade was about discovering, painfully, that the world is not closed.</p><h2>The cost of winning too hard</h2><p>In 1969, Minsky and Papert published <em>Perceptrons</em> and proved a single-layer network can&#8217;t compute XOR. </p><p>The proof was correct. It was also narrow &#8212; they admitted multi-layer networks could do it, nobody just knew how to train them yet. But the field was hungry for a verdict, and it read the book as one. Funding for neural networks collapsed. Rosenblatt died two years later in a boating accident, on his 43rd birthday. The algorithm that would resurrect his branch didn&#8217;t arrive at scale until 1986. Seventeen years of silence.</p><p>Modern AI runs on the work of people who weren&#8217;t born when Minsky and Papert published. The reason their work came so late is that the field they&#8217;d return to had been kept near-dead for two decades. The symbolic camp&#8217;s victory was real. The field paid for it. It will pay that bill again.</p><h2>The rationalist trap</h2><p>Through the 1970s and 80s the symbolic branch found something that made money: <strong>expert systems</strong>. </p><p>MYCIN matched infectious-disease specialists. XCON saved DEC tens of millions a year. The thesis was clean and seductive &#8212; intelligence is rules plus facts; hire the expert, extract the rules, ship the system. And these systems were <em>legible</em>. You could read every rule, audit the reasoning, fix the wrong line. (Your favorite large language model cannot do this. We&#8217;ll come back to that another day.)</p><p>Two problems killed it. Common sense turns out to be unrepresentable in rules &#8212; <em>birds fly, except penguins, except baby penguins, except dead ones</em> &#8212; and the rules contradict each other faster than you can write them. And then there&#8217;s Cyc: in 1984 Doug Lenat set out to hand-encode all of common-sense knowledge, estimated ten years, and is still at it forty-two years later. It is the most thoroughly humbling monument in the history of cognitive science. </p><p>By the late 80s the money dried up and the Second AI Winter set in. The field was tired of the rationalists.</p><h2>The empirical rebellion</h2><p>We&#8217;re on 1986: backpropagation, in <em>Nature</em>, multi-layer networks are finally trainable. </p><p>Then the empiricist branch spends fifteen years not scoring one big win but a thousand small ones. Support vector machines. Random forests. Boosting. Statistical methods quietly eating one application after another, including the symbolic camp&#8217;s home turf &#8212; language, where IBM&#8217;s speech team found that every time they <em>fired</em> a linguist, the system improved.</p><p>Why now? Three things are moving together, slowly. Compute grew. The internet started producing data in volumes nobody had imagined. And the methods were simple enough to scale with both. </p><p>In 2019 Richard Sutton would name this <em>The Bitter Lesson</em>: across seventy years, the general method that scales with compute beats the clever hand-engineered one, every time. It&#8217;s bitter because it tells researchers their hard-won taste gets steamrolled by someone with more GPUs. It is <em>mostly</em> right.</p><p> The thing that complicates it is the thing symbolic AI was good at all along &#8212; but I&#8217;m getting ahead of myself.</p><h2>The earthquake</h2><p>Now jump to September 2012. </p><p>AlexNet &#8212; eight layers, two gaming GPUs, a couple of training tricks &#8212; drops the ImageNet error rate ten points below the nearest hand-engineered system. A ten-point gap isn&#8217;t an improvement. It&#8217;s a different category of result. Within six months every computer-vision lab on Earth has pivoted. AlexNet is, by a wide margin, the single most consequential paper in modern AI.</p><p>Then it cascades, almost too fast to track. Sequence-to-sequence translation. GANs. Atari from raw pixels. In 2016 <strong>AlphaGo</strong> beats Lee Sedol at a game with more board positions than there are atoms in the universe &#8212; and almost nobody notices that inside it is a deep network (empiricist) wrapped around a tree search (symbolic). The marriage is <em>already there</em>, in 2016, hiding in plain sight. In 2017, &#8220;Attention Is All You Need&#8221; introduces the Transformer, and every model in your chat window today descends from that one paper.</p><h2>The crown jewel nobody talks about</h2><p>The most consequential AI system of the modern era is not a chatbot. It doesn&#8217;t write poems. It&#8217;s in the bloodstream of structural biology.</p><p>Predicting a protein&#8217;s 3D shape from its amino-acid sequence is a fifty-year-old problem. The rationalist approach &#8212; simulate the physics &#8212; was beautiful and almost completely intractable. For twenty years the field&#8217;s hardest benchmark plateaued at a score around 40. In 2020, DeepMind&#8217;s <strong>AlphaFold 2</strong> scored above 92 on that exact tier. The grand challenge was, for practical purposes, solved. Hassabis and Jumper got the 2024 Nobel in Chemistry for it &#8212; <em>the only AI work so far to produce a Nobel-level scientific breakthrough</em>. </p><p>Read the citation. It isn&#8217;t about AI as a technology. It&#8217;s about a problem that got finished while the people whose careers were defined by it slept. The chatbots get the headlines. The image generators get the lawsuits. The protein folder got the world. Remember that the next time someone wants to tell AI is ChatGPT.</p><h2>The synthesis</h2><p>Now the marriage. The word <em>agent</em> did not come from machine learning. It came from classical, symbolic AI in the 1970s and 80s: a system that perceives its environment, deliberates, picks an action, acts, observes, loops. The architecture was right. The brain was missing. Pure symbolic computation could never model a world with grandparents and coffee in it, so the agent shell sat there for decades, structurally correct and operationally empty. Cyc, again, is the long sad proof.</p><p>The empiricists borrowed the same word in the 2000s &#8212; in reinforcement learning, an <em>agent</em> is a learned policy. DQN was an agent. AlphaGo was an agent. A new brain, slotted into the old shell. Spectacular, and narrow. An AlphaGo cannot make you a sandwich.</p><p>In 2024 the cognition slot gets filled a third time, by a general-purpose reasoning language model. The shell is still the seventy-year-old symbolic frame: perceive, deliberate, pick an action with a name and a meaning &#8212; <code>read_file</code>, <code>run_tests</code>, <code>send_email</code> &#8212; act, observe, loop. The brain is now an LLM. From the empiricist side the system inherits flexibility: it has read enough of the world that you don&#8217;t have to tell it what a file is, or what an angry customer sounds like. From the symbolic side it inherits structure: the actions have names, the consequences are bounded, the trajectory is auditable. The model can hallucinate; the system can&#8217;t run <code>rm -rf</code> unless somebody wired that action in and granted it.</p><p>The 1970s symbolic agent could never reason. The 2010s RL agent could never generalize. The 2026 agent does both &#8212; badly, often clumsily, but for the first time at the same time. You can watch this happen most clearly in software development right now: a language model at the core, a harness of tools around it, a test suite as the verifier, a human reviewing the diff. All four layers, on a laptop, today. Software is the canary. The same pattern is already moving toward research, then education, then everything whose feedback loops are fast enough.</p><p>So here is the closing claim, the techno-pragmatist version of the whole story. The seventy-year argument did not produce a winner. It produced three layers: a learning substrate that absorbed the written record of humanity, a symbolic shell that makes it accountable, and a human frame that decides what the whole thing is <em>for</em>. The first two are engineering. The third is the only one that was ever really about us.</p><p>The synthesis exists. What we do with it is still up to us.</p><p>Until next time, <strong>stay curious</strong>.</p><div><hr></div><p><em>This post is the speedrun &#8212; the book&#8217;s ~8,000-word opening chapter compressed down to its spine. The full version in the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a> has every scene with its characters, the eras this post skipped, 70+ references, and the agentic stack the rest of the book then takes apart mechanism by mechanism. It&#8217;s the book I wish someone had handed me when I was trying to make sense of the noise &#8212; and it&#8217;s 50% off while it&#8217;s in early access. You can also <a href="https://books.apiad.net/books/mhai/">read the whole thing online for free</a> in a custom reader I built and am rather proud of: dark mode, font controls, progress tracking, offline support, the works.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/mhai/fiftyoff&quot;,&quot;text&quot;:&quot;Get it (50% off)&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/mhai/fiftyoff"><span>Get it (50% off)</span></a></p><p><em>And if you want the whole catalog of everything I&#8217;ve written, plus everything I&#8217;m going to write, that&#8217;s <a href="https://apiad.gumroad.com/l/compendium">the Compendium</a>. One purchase, in perpetuity.</em></p>]]></content:encoded></item><item><title><![CDATA[How Large Languages Models Are Really Made]]></title><description><![CDATA[The full road from text data to reasoning models, explained visually with zero math or code.]]></description><link>https://blog.apiad.net/p/mhai-llms</link><guid isPermaLink="false">https://blog.apiad.net/p/mhai-llms</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Mon, 11 May 2026 15:51:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!gdqf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gdqf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gdqf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gdqf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gdqf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gdqf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gdqf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!gdqf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gdqf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gdqf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gdqf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb83eb00a-2839-4312-8300-5fbf71095e63_1376x768.jpeg 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><figcaption class="image-caption"><em>After Joseph Racknitz&#8217;s 1789 <a href="https://en.wikipedia.org/wiki/Mechanical_Turk">cutaway engraving</a> of Wolfgang von Kempelen&#8217;s Mechanical Turk; generated with Nano Banana 2.</em></figcaption></figure></div><p>You type a message to an AI assistant and it answers. The answer isn&#8217;t looked up. It isn&#8217;t scripted. The model generated it, character by character, guided by a single mathematical question: <em>what comes next?</em></p><p>That question is the foundation of every language model ever built. A <strong>language model</strong> is a probability distribution over text &#8212; a function that, given a sequence of words, assigns a probability to every possible continuation. &#8220;The cat sat on the mat&#8221; scores higher than &#8220;the mat sat on the cat&#8221; not because a language model understands what cats do, but because the first sequence appears in human text and the second doesn&#8217;t. The model has compressed the co-occurrence patterns of an enormous corpus into its weights, and that compression is what produces a score.</p><p>Generative: you give the model a prefix, it samples the highest-probability next token, appends it, samples again, and repeats until a stop token arrives. The multi-paragraph response you got from ChatGPT this morning was that loop running a few hundred times. No lookup table. No if-else tree. No pre-scripted answers. Just: <em>given all of this text, what is most likely to come next?</em></p><p>Here&#8217;s the thing I find quietly strange about this: it works. A procedure this simple &#8212; assign probabilities, sample the maximum &#8212; has produced the most influential technology of the last decade. What makes it work isn&#8217;t the procedure. It&#8217;s everything that goes into building a probability distribution that&#8217;s actually <em>good</em>. Good enough to write coherent paragraphs. Good enough to reason about code. Good enough to pass the bar exam and explain quantum mechanics in language your parents can follow.</p><p>Getting there took decades of compounding ideas. The arc is what this piece covers &#8212; from the crudest possible approximation of &#8220;probability over text&#8221; to the current frontier, where models are learning to think.</p><p>Each section of what follows is best understood as a response to the failure of the previous one. N-gram models worked until they didn&#8217;t. Neural embeddings fixed the part that broke. Pretraining scaled the fix to the size where it became genuinely impressive. Instruction tuning made the result useful for the first time. Preference learning fixed what instruction tuning couldn&#8217;t. Reasoning models added something nobody was sure was trainable at all.</p><p>Seven steps. One direction.</p><blockquote><p><em>Every post on the blog this month is on the theme of agent reliability, anchored on the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a>, where the engineering details that don&#8217;t fit a blog post live. You can also <a href="https://books.apiad.net/books/mhai/">read the whole book online for free</a> in a custom reader I built. More at the end.</em></p></blockquote><div><hr></div><blockquote><p>This post may be truncated in your email. <a href="https://blog.apiad.net/p/mhai-llms">Read it online</a> for the best experience.</p></blockquote><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>The n-gram intuition</h2><p>The simplest possible implementation of &#8220;probability over text&#8221; is a lookup table.</p><p>Take a large corpus &#8212; a hundred million words will do to start. For every three-word sequence (<strong>trigram</strong>) in that corpus, record which word follows it most often, and with what frequency. &#8220;The quick brown&#8221; &#8594; &#8220;fox,&#8221; nine times out of ten, because Project Gutenberg is full of that particular sentence. &#8220;The capital of&#8221; &#8594; &#8220;France&#8221; thirty percent of the time, &#8220;Germany&#8221; twelve, &#8220;England&#8221; eleven, and so on through the geography. For every trigram you&#8217;ve seen, you have a probability distribution over what comes next.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CQfE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CQfE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.png 424w, https://substackcdn.com/image/fetch/$s_!CQfE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.png 848w, https://substackcdn.com/image/fetch/$s_!CQfE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.png 1272w, https://substackcdn.com/image/fetch/$s_!CQfE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CQfE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.png" width="1456" height="929" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:929,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!CQfE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.png 424w, https://substackcdn.com/image/fetch/$s_!CQfE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.png 848w, https://substackcdn.com/image/fetch/$s_!CQfE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.png 1272w, https://substackcdn.com/image/fetch/$s_!CQfE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bf3e3f-4e4b-4115-9ccf-54ee5a394136_1600x1021.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><figcaption class="image-caption"><em>A trigram model is a frequency dictionary: each three-word context maps to a distribution over possible next words. The catch is that a 50,000-word vocabulary admits ~126 trillion possible trigrams, most of which never appear in any corpus. Made with <a href="https://github.com/apiad/tesserax">tesserax</a>.</em></figcaption></figure></div><p>Now generate text. Give the model &#8220;The quick brown&#8221; and it continues: &#8220;fox.&#8221; Give it &#8220;jumped over the&#8221; and it continues: &#8220;lazy.&#8221; Three words in and it&#8217;s generated &#8220;The quick brown fox jumped over the lazy&#8221; &#8212; and if you&#8217;re lucky, it lands &#8220;dog&#8221; and you&#8217;ve reproduced a famous sentence entirely from corpus statistics. Locally, it&#8217;s plausible. You could read a sentence of this and not immediately know you&#8217;re looking at a machine.</p><p>The problems start fast. By the third sentence, the model has no idea it was talking about a fox. It only remembers the last three words. You ask it to continue &#8220;The fox had been running from the&#8221; and it has no idea that a fox is involved, or that running happened, or that there&#8217;s a pursuit in progress. It just has three words and a lookup table. The output is <em>statistically English</em>. It is not coherent.</p><p>This is the <strong>Markov assumption</strong>: the next word depends only on the last N words, not on the full history of the text. For N=3, it&#8217;s a trigram model. You can increase N &#8212; five-gram models were standard in commercial speech recognition for years &#8212; but the table explodes. Fifty thousand words in the English vocabulary gives 50,000&#179; possible trigram contexts, roughly 125 trillion entries. At N=10, the number of possible sequences exceeds the estimated number of atoms in the observable universe. The table can never be complete enough to cover the distribution.</p><p>There&#8217;s a real engineering solution to the &#8220;we haven&#8217;t seen this exact trigram&#8221; problem: smoothing and interpolation. Estimate the probability of an unseen N-gram from shorter sub-sequences. <strong>Hidden Markov models</strong> formalised this in a probabilistic framework that, by the 1990s, had enough polish to power industrial speech recognition and early machine translation. I don&#8217;t want to undersell it &#8212; it worked. It was genuinely useful. It just topped out.</p><p>The wall is fundamental. Real language has dependencies that can be arbitrarily long. <em>&#8220;The man who sold the car that had been parked in front of the house where my grandmother lived was finally found.&#8221;</em> The subject of &#8220;was finally found&#8221; is seventeen words and three nested clauses back. No N-gram model reaches it. You need something that can condition on the full context &#8212; or at least compress the full context intelligently &#8212; rather than amnesiacally forget everything more than N words ago.</p><p>You need a model that generalises from sequences it has seen to sequences it hasn&#8217;t. A lookup table can only interpolate from what it&#8217;s seen before. What you need is something that has <em>understood the pattern</em> deeply enough to extrapolate.</p><p>N-gram models work until they don&#8217;t &#8212; and they don&#8217;t beyond a few words.</p><h2>Words as numbers</h2><p>Neural networks can learn the compression n-gram models can&#8217;t achieve. But they have a hard prerequisite: they operate on numbers. Words are symbols. Before a neural network can do anything useful with text, you need to represent words as vectors. The naive approach throws away everything that matters.</p><p>The obvious first attempt is <strong>one-hot encoding</strong>. Vocabulary of 50,000 words; each word is a vector of length 50,000 with a single 1 and 49,999 zeros. &#8220;Cat&#8221; is at position 4,312; &#8220;dog&#8221; is at position 17,846; &#8220;carburetor&#8221; is somewhere else entirely. The problem: nothing in this representation suggests that &#8220;cat&#8221; and &#8220;dog&#8221; are more similar to each other than either is to &#8220;carburetor.&#8221; The distance between every pair of one-hot vectors is identical. You&#8217;ve handed the network a symbol system with no structure, and it has to reconstruct the structure from scratch &#8212; spending enormous capacity learning that cats and dogs are both animals, that both appear near &#8220;fur&#8221; and &#8220;vet,&#8221; that &#8220;cat food&#8221; and &#8220;dog food&#8221; are structurally related &#8212; before it can learn anything about how language actually works.</p><p>The key insight that resolved this came from linguistics, not machine learning, and I think it&#8217;s underrated as an idea. J.R. Firth, writing in 1957: <em>&#8220;you shall know a word by the company it keeps.&#8221;</em> The <strong>distributional hypothesis</strong>. Words that appear in similar contexts &#8212; near similar neighbouring words, in similar grammatical positions &#8212; tend to have similar meanings. &#8220;Cat&#8221; and &#8220;dog&#8221; both appear near &#8220;pet,&#8221; &#8220;feed,&#8221; &#8220;vet,&#8221; &#8220;owner,&#8221; &#8220;fur,&#8221; &#8220;collar.&#8221; The context is a fingerprint of the meaning. Encode that fingerprint in a vector and you have a representation where similar words land close together in space.</p><p><strong>Word2Vec</strong> (<a href="https://arxiv.org/abs/1301.3781">Mikolov et al., 2013</a>) turned this into a training procedure. Train a shallow neural network to predict a word from its surrounding context words, or vice versa. Force each word&#8217;s representation down into a dense vector of, say, 300 floating-point numbers. Train on a billion words of text. The network learns that words appearing in similar contexts should have similar representations, because that&#8217;s what makes the prediction task cheaper. Words with similar distributional patterns end up with similar vectors &#8212; not because anyone programmed that, but because it follows from the objective.</p><p>The result that made people pay attention: <strong>vector arithmetic encodes semantic relationships</strong>. Take the vector for &#8220;king,&#8221; subtract the vector for &#8220;man,&#8221; add the vector for &#8220;woman.&#8221; The nearest vector in the resulting space is &#8220;queen.&#8221; Paris minus France plus Italy is approximately Rome. Try it yourself: it works because the structural relationship between &#8220;king&#8221; and &#8220;queen&#8221; is parallel to the relationship between &#8220;man&#8221; and &#8220;woman&#8221; in how the four words co-occur with everything around them. No one wrote these analogies in. The geometry of the space mirrors the structure of meaning, because both are implicit in how words appear together in natural language.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KXh1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KXh1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.png 424w, https://substackcdn.com/image/fetch/$s_!KXh1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.png 848w, https://substackcdn.com/image/fetch/$s_!KXh1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.png 1272w, https://substackcdn.com/image/fetch/$s_!KXh1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KXh1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.png" width="458" height="396.97527472527474" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1262,&quot;width&quot;:1456,&quot;resizeWidth&quot;:458,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!KXh1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.png 424w, https://substackcdn.com/image/fetch/$s_!KXh1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.png 848w, https://substackcdn.com/image/fetch/$s_!KXh1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.png 1272w, https://substackcdn.com/image/fetch/$s_!KXh1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75fb3c1-35e7-49f9-915b-6bdd8334bef2_1600x1387.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><figcaption class="image-caption"><em>The relationship between</em> king <em>and</em> queen <em>is parallel to the one between</em> man <em>and</em> woman <em>&#8212; both fall out of how the four words co-occur with everything around them. Made with <a href="https://github.com/apiad/tesserax">tesserax</a>.</em></figcaption></figure></div><p>I find this genuinely strange, in the best possible way. You trained a network to do a simple word-guessing task on flat text, and the side effect was an algebra of concepts. The geometry was always latent in the co-occurrence patterns. Word2Vec just made it legible.</p><p>Modern language models don&#8217;t use Word2Vec as a separate preprocessing step &#8212; the embedding representations are learned jointly with the rest of the network during training on text. But Word2Vec&#8217;s intuition is <em>why</em> learned embeddings work at all. Once language is geometry, gradient descent has a surface to grip. You can compute distances, optimise them, stack arbitrarily deep networks on top, and train the whole thing end-to-end.</p><p>Embeddings are how we lie to neural networks in a useful way. We pretend words are points in space, so the math works out.</p><h2>Pretraining</h2><p>Now scale it.</p><p>Take a deep neural network &#8212; not the shallow two-layer thing in Word2Vec, but a transformer with dozens or hundreds of layers, billions of parameters, and an attention mechanism in every one of them. Feed it next-token prediction across the entire accessible internet: Wikipedia, GitHub, every book ever digitised, every forum thread, every research paper, every recipe, every political argument, every user manual for every piece of machinery ever manufactured. Same objective the n-gram model had: <em>given what came before, what comes next?</em> Except now the model has billions of parameters to compress the patterns into, the training signal is trillions of tokens, and the architecture is built to handle arbitrarily long context.</p><p>The architecture is what made everything else possible. The <strong>transformer</strong> (<a href="https://arxiv.org/abs/1706.03762">Vaswani et al., 2017</a>) uses <strong>self-attention</strong> as its core operation. For each token in the input, self-attention computes relevance weights over every other token in the sequence &#8212; learned weights, computed from the data, different for each token, different in each layer. A pronoun can attend strongly to the noun it refers to, twenty positions back. A closing argument can reach back to the premise from the opening paragraph. There is no fixed window; the model considers, in principle, the full context at every step.</p><p>This is what broke the n-gram scaling wall. Not a bigger lookup table. Not smarter interpolation. A learned, flexible attention mechanism that compresses long-range dependencies into the model&#8217;s weights rather than trying to enumerate every possible context sequence. The key property, and it&#8217;s the one I keep coming back to: <em>soft</em>. Self-attention doesn&#8217;t pick one relevant token; it blends all of them with learned weights. The whole sequence contributes to every prediction, with a learned notion of how much each part matters.</p><p>The other critical property is <strong>self-supervised learning</strong>. There are no human-provided labels anywhere in pretraining. The text itself is the training signal. Show the model &#8220;The capital of&#8221; and ask it to predict &#8220;France.&#8221; It&#8217;s wrong; the gradient flows; the weights update. Show it three trillion tokens; let the gradient flow three trillion times. The entire digitised corpus of human knowledge is your training set, with zero labelling cost, because the next token is always right there.</p><p><strong><a href="https://arxiv.org/abs/2001.08361">Kaplan et al., 2020</a></strong> measured loss as a function of model size, dataset size, and compute over seven orders of magnitude. The result: loss falls as a clean power law across all three dimensions. Double the parameters, get a predictable drop in loss. Double the training data, same. Scale is not a bet on something uncertain; it is a known return on investment, measured and re-measured across a staggering range.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-3Ei!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-3Ei!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.png 424w, https://substackcdn.com/image/fetch/$s_!-3Ei!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.png 848w, https://substackcdn.com/image/fetch/$s_!-3Ei!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.png 1272w, https://substackcdn.com/image/fetch/$s_!-3Ei!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-3Ei!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.png" width="490" height="289.08653846153845" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:859,&quot;width&quot;:1456,&quot;resizeWidth&quot;:490,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!-3Ei!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.png 424w, https://substackcdn.com/image/fetch/$s_!-3Ei!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.png 848w, https://substackcdn.com/image/fetch/$s_!-3Ei!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.png 1272w, https://substackcdn.com/image/fetch/$s_!-3Ei!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adcc87d-c1ca-467b-b091-9e17e61cd52a_1600x944.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><figcaption class="image-caption"><em>Schematic of the Kaplan finding: loss vs. compute on log-log axes. The points fall on a clean line over eight decades of compute &#8212; scale isn&#8217;t a bet, it&#8217;s a known return. Made with <a href="https://github.com/apiad/tesserax">tesserax</a>.</em></figcaption></figure></div><p><strong><a href="https://arxiv.org/abs/2203.15556">Hoffmann et al., 2022</a></strong> &#8212; the Chinchilla paper &#8212; corrected a real error in how the field had been applying Kaplan&#8217;s result. Most large models of 2020-21 had been trained on far fewer tokens than their parameter count justified. The Kaplan result suggested scaling up models as fast as possible; Hoffmann&#8217;s finding was that you need to scale model size <em>and</em> training data together, roughly equally, for a given compute budget. A 70B-parameter model trained on 1.4 trillion tokens outperformed a 280B-parameter model trained on fewer tokens at the same total compute cost. Smaller model, more data, better result. Scale both dimensions together.</p><p>What do you get at the end of all this? A <strong>base model</strong>. And this is the part that surprises people who haven&#8217;t seen one.</p><p>Type &#8220;What is the capital of France?&#8221; into a raw pretrained model and it continues the text. Maybe it writes out a geography quiz &#8212; &#8220;What is the capital of France? What is the capital of Germany? What is the capital of Italy?&#8221; Maybe it generates a fake Wikipedia article. Maybe it starts a trivia show transcript. It has not answered your question. It has found the most probable continuation of your prompt, given everything it absorbed during training.</p><p>The base model has absorbed more text than any human could read in a thousand lifetimes. The co-occurrence patterns of the entire digitised corpus of human writing are in those weights. It knows facts, relationships, styles, concepts, code, chemistry, poetry, legal prose, and every other form in which humans have arranged words.</p><p>It was trained to <em>continue</em>, not to <em>respond</em>. Ask it a direct question and it treats the question as the opening line of some text pattern &#8212; one it will extend in whatever direction seems most probable. It has no concept of &#8220;you asked me something and I should answer it.&#8221;</p><p>Pretraining gives a model knowledge. It does not give it manners, opinions, or any idea what you want from it.</p><h2>Instruction tuning</h2><p>Step one of making a base model useful: show it what &#8220;useful&#8221; looks like.</p><p>Collect thousands of demonstration pairs. A human writer sits with a prompt &#8212; &#8220;Explain the difference between supervised and unsupervised learning in plain English,&#8221; &#8220;Write a polite email declining this meeting invitation,&#8221; &#8220;Debug this Python function&#8221; &#8212; and writes the ideal response. Then fine-tune the pretrained base model on these (prompt, response) pairs using the same next-token objective, now applied to curated demonstrations rather than the open web.</p><p>This is <strong>supervised fine-tuning</strong>, or SFT. It is plain supervised learning &#8212; the same paradigm that has been in the machine learning textbooks since the 1980s. What&#8217;s new is only what it&#8217;s being applied to.</p><p>The headline result from <a href="https://arxiv.org/abs/2203.02155">Ouyang et al., 2022</a> &#8212; the InstructGPT paper &#8212; is still worth stating plainly: a 1.3 billion-parameter model, fine-tuned on human-written instruction-following demonstrations, was <em>preferred</em> by human evaluators over a raw 175 billion-parameter GPT-3. One percent of the parameters. Preferred.</p><p>Sit with that. The quality of the training signal matters more than raw scale. A carefully curated set of demonstrations of what &#8220;helpful answering&#8221; looks like is worth more, for the specific goal of being helpful, than a hundred times more parameters trained on unstructured internet text. The base model knows more. The instruction-tuned model is more useful. These are different things.</p><p>SFT teaches the <em>shape</em> of a helpful answer: addressed to the question asked, reasonably structured, proportionate in length, appropriate in tone. These are learnable patterns. The base model already has all the relevant knowledge in its weights; SFT is teaching it to retrieve and present that knowledge in a particular format.</p><p>Here&#8217;s the failure mode, and it matters for understanding everything that comes next.</p><p>SFT shows the model what good answers look like. It gives no mechanism for the model to evaluate, <em>at generation time</em>, which of two candidate continuations is more accurate, more honest, or less likely to cause harm. The model learned to imitate the shape of correct answers; it did not learn to <em>prefer</em> correctness over fluency when the two conflict. A confidently phrased wrong answer and a confidently phrased right answer can look identical from a format standpoint. SFT cannot distinguish them.</p><p>Teaching consistent refusals is especially brittle. To get a model to reliably refuse a class of harmful requests via SFT, you need human-written refusals for every phrasing variant you can anticipate. You will miss variants. The model has no general theory of harm. It has only pattern-matching against the phrasings it saw. Change the phrasing, add a fictional framing, ask in a different language, and the refusal can fail.</p><p>The deep limitation is this: SFT can teach what a good answer looks like, but it cannot teach which of two candidate answers is <em>better</em>. For that, you need to know something about <em>better</em> that you didn&#8217;t encode in any single example. You need preferences.</p><p>SFT teaches the shape of a good answer. It has no way to choose between two good shapes.</p><h2>From demonstrations to preferences</h2><p>The move that follows from SFT&#8217;s failure: what humans can do <em>faster</em> than writing demonstrations is ranking them.</p><p>Show a rater two model responses to the same prompt &#8212; response A and response B &#8212; and ask which is better. They can answer in seconds. Writing a response from scratch takes minutes. This means you can collect preference labels at much higher volume than demonstrations, and the preference label contains a different kind of information: not &#8220;here is the target,&#8221; but &#8220;this is closer to the target than that.&#8221;</p><p>Scale up the preference collection. Collect hundreds of thousands of (prompt, response A, response B, human ranking) tuples. Train a small auxiliary model &#8212; the <strong>reward model</strong> &#8212; to predict the human rankings: given a prompt and a response, output a scalar score. Then use <strong>reinforcement learning</strong> (specifically PPO) to push the language model toward generating responses the reward model scores highly.</p><p>This is <strong>RLHF</strong> &#8212; reinforcement learning from human feedback. <a href="https://arxiv.org/abs/2203.02155">Ouyang et al., 2022</a> used it as the third stage of the InstructGPT pipeline: pretraining &#8594; SFT &#8594; RLHF. ChatGPT&#8217;s characteristic tone &#8212; helpful, reliably cautious about harmful requests, good at hedging uncertainty, consistent about refusals &#8212; comes almost entirely from this stage.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CtE_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CtE_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.png 424w, https://substackcdn.com/image/fetch/$s_!CtE_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.png 848w, https://substackcdn.com/image/fetch/$s_!CtE_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.png 1272w, https://substackcdn.com/image/fetch/$s_!CtE_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CtE_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.png" width="1456" height="574" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:574,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!CtE_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.png 424w, https://substackcdn.com/image/fetch/$s_!CtE_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.png 848w, https://substackcdn.com/image/fetch/$s_!CtE_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.png 1272w, https://substackcdn.com/image/fetch/$s_!CtE_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c8d7d72-6a41-44dc-8429-be45acad45d1_1600x631.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><figcaption class="image-caption"><em>RLHF in five stages: the model samples two responses, a human ranks them, a reward model learns to predict the ranking, and PPO updates the policy to maximise that reward. DPO collapses the loop &#8212; the preference becomes a loss applied directly on the policy, skipping both the reward model and PPO. Made with <a href="https://github.com/apiad/tesserax">tesserax</a>.</em></figcaption></figure></div><p>The deepest shift over SFT: the model now has feedback about <em>direction</em>, not just target. SFT says &#8220;produce something like this example.&#8221; RLHF says &#8220;of the things you just produced, this kind is better than that kind &#8212; adjust accordingly.&#8221; A direction is a richer signal than a target. It can propagate to novel situations no demonstration ever covered.</p><p>The practical problem is that RLHF is a genuine engineering challenge. PPO is unstable. The reward model can be <strong>gamed</strong>: the policy learns to produce outputs that score highly on the reward model without actually being better, because the reward model is an imperfect proxy for true quality. Over long training runs, the policy finds exploitable features in the reward model and optimises for those rather than for what humans actually wanted. Balancing the RL update against the pretrained base (so the model doesn&#8217;t drift into incoherence while chasing reward) requires careful tuning. RLHF works, but it&#8217;s expensive, brittle, and hard to reproduce without a dedicated ML infrastructure team behind it.</p><p><a href="https://arxiv.org/abs/2305.18290">Rafailov et al., 2023</a> found something that, in retrospect, looks almost obvious: you can skip the reward model entirely.</p><p>The paper, &#8220;Direct Preference Optimization: Your Language Model is Secretly a Reward Model,&#8221; makes a mathematical observation. The preference-fitting problem that RLHF solves via a reward model + PPO can be reformulated as a classification loss directly on the language model policy. Given a preferred response and a dispreferred response to the same prompt, you want the model to be more likely to produce the preferred one. You don&#8217;t need a separate reward model to express that preference. You don&#8217;t need PPO to optimise it. The preference is a loss; the loss can be minimised directly on the policy.</p><p><strong>DPO</strong> is computationally lighter and far easier to get working. The abstract says it &#8220;eliminates the need for sampling from the LM during fine-tuning or performing significant hyperparameter tuning.&#8221; In practice, the gap between &#8220;has the resources of a frontier lab&#8221; and &#8220;fine-tuned a model that actually behaves well&#8221; narrowed substantially after DPO. Within a year of publication, it became the default alignment approach for most open-weight instruct models &#8212; the Llama family, Mistral, Qwen, and most of their derivatives. When you read that a model is &#8220;instruction-tuned&#8221; in 2024 or later, DPO or one of its descendants is almost always in the pipeline.</p><p>Both RLHF and DPO require human preference labels. In 2022-23, this was tractable. By 2024, at frontier scale, it was a real bottleneck. You need raters capable of judging quality on maths, code, medicine, science. You can&#8217;t hire enough such raters to keep pace with the rate at which models can generate candidate outputs.</p><p>The field&#8217;s response was predictable in retrospect: use the models themselves.</p><p><strong>RLAIF</strong> (<a href="https://arxiv.org/abs/2309.00267">Lee et al., 2023</a>) replaces human raters with a strong language model as the preference oracle. Head-to-head comparisons with RLHF showed that AI-generated preference labels are competitive with human ones on summarisation and dialogue. The reward model trained on AI labels performs comparably to the one trained on human labels. The human rater is no longer in the loop.</p><p><strong>Constitutional AI</strong> (<a href="https://arxiv.org/abs/2212.08073">Bai et al., 2022</a>, Anthropic) does something more principled. Write a list of principles &#8212; a constitution &#8212; stating what the model should and shouldn&#8217;t do. Ask the model to critique its own outputs against those principles and revise them. The critiques and revisions become training data. The RL stage uses the model&#8217;s own evaluations as the reward signal. Human preference labelling is replaced by explicit normative reasoning: the model has to argue about whether its outputs satisfy the stated principles, not just produce outputs that pattern-match to human-labelled examples.</p><p>The logic extends one step further. If models can generate reliable preference labels, can they generate <em>training data</em> directly? By 2024-25, the answer in a widening range of domains was yes. Maths problems with worked solutions. Code problems paired with passing test suites. Instruction-following demonstrations written by large models to train smaller ones &#8212; the distillation pipeline, where a 70B model generates training examples that improve a 7B model, and the better 7B model feeds the next iteration. By 2025, a substantial fraction of the data used to train frontier models isn&#8217;t scraped from the web. It&#8217;s generated by earlier versions of the models themselves.</p><p>Ilya Sutskever, speaking at <a href="https://www.youtube.com/watch?v=WGgDZOr1ph4">NeurIPS 2024</a>: <em>&#8220;Pre-training as we know it will end. Data is the fossil fuel of AI. We have but one internet.&#8221;</em> The scaling curve that had defined the field since Kaplan 2020 was visibly flattening. The field didn&#8217;t slow down. The growth frontier moved: from bigger pretraining to better post-training. The headline AI announcements of 2024 were not &#8220;we trained a bigger model on more of the internet.&#8221; They were &#8220;we trained a better model by using our previous models to generate, evaluate, and curate the training signal.&#8221;</p><p>The sharpest shift in AI in 2023-24 wasn&#8217;t a bigger model. It was figuring out how to use models to train better models.</p><h2>A new axis</h2><p>There is a 2024-25 discovery that changes the picture in a qualitatively different way. Not a refinement of post-training preference optimisation. Something new.</p><p>RL doesn&#8217;t just align models. It can teach them to <em>think</em>.</p><p>The observation that sets it up: language models already have scratch space. Their output is text; nothing prevents them from writing intermediate reasoning steps before writing a final answer. Chain-of-thought prompting &#8212; asking a model to &#8220;think step by step&#8221; &#8212; has been known since <a href="https://arxiv.org/abs/2201.11903">2022</a> to improve performance on reasoning tasks. The model writes out intermediate steps, and those steps help it arrive at a better final answer.</p><p>But chain-of-thought as a <em>prompting technique</em> has a persistent problem. The intermediate steps are generated by the same forward pass as the final answer. You can ask the model to think out loud, but you can&#8217;t verify that the scratchpad is doing reasoning work rather than <em>performing</em> reasoning for the reader. A model that writes plausible-sounding intermediate steps that happen to be wrong, then arrives at a wrong final answer, has not improved by being asked to show its work. The steps are decorative.</p><p>The <a href="https://openai.com/index/learning-to-reason-with-llms/">o-series models from OpenAI</a> in late 2024 made a conceptually simple training move: use RL where the reward is the correctness of the <em>final answer</em>, and leave the intermediate chain of thought entirely unsupervised. The model can write whatever it wants in the scratchpad. The only signal is whether the final answer is right.</p><p>What emerged from training was not what anyone programmed in. The model learned, without any explicit supervision of the intermediate steps, to <em>use</em> the scratchpad as actual working memory. Backtracking when an approach failed. Trying alternate formulations when one hit a wall. Verifying intermediate results before continuing. Restarting from scratch, several steps back, when it found an error in something it had already written. None of these behaviours appeared in labelled training examples. They fell out of the objective: over enough RL iterations, the training process found that careful scratchpad use led to more correct final answers, and it reinforced that.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gXIs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gXIs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.png 424w, https://substackcdn.com/image/fetch/$s_!gXIs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.png 848w, https://substackcdn.com/image/fetch/$s_!gXIs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.png 1272w, https://substackcdn.com/image/fetch/$s_!gXIs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gXIs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.png" width="1456" height="600" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:600,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!gXIs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.png 424w, https://substackcdn.com/image/fetch/$s_!gXIs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.png 848w, https://substackcdn.com/image/fetch/$s_!gXIs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.png 1272w, https://substackcdn.com/image/fetch/$s_!gXIs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da4f71b-6e76-41ad-93c4-9db3b062e8d3_1600x659.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><figcaption class="image-caption"><em>Only the final answer receives reward. The scratchpad is entirely unsupervised &#8212; backtracking and verification fall out of the training loop, not from labelled examples of good reasoning. Made with <a href="https://github.com/apiad/tesserax">tesserax</a>.</em></figcaption></figure></div><p><strong>DeepSeek-R1</strong> (<a href="https://arxiv.org/abs/2501.12948">arXiv:2501.12948</a>, January 2025, open weights) replicated the result outside a closed lab. Pure RL on reasoning trajectories; no human-labelled chains required. The paper documents what they call the &#8220;aha moment&#8221; in training: a specific point where the model spontaneously began verifying its own intermediate steps and restarting when they failed. Not because the training data contained this behaviour as a pattern. Because the reward for correct final answers made careful intermediate reasoning instrumentally useful, and the RL training loop discovered it.</p><p>I think DeepSeek-R1 is the clearest published demonstration of something the field had suspected but hadn&#8217;t proven at scale: that reasoning, as a <em>behaviour</em>, is trainable from a simple outcome-based reward signal. You don&#8217;t need human annotations of good reasoning traces. You don&#8217;t need to supervise the scratchpad. You need to reward the right answer and run enough RL. The reasoning emerges.</p><p>Three things follow from this that are worth naming separately.</p><p><strong>Test-time compute</strong> is a new scaling axis. Pretraining scales with more data and more parameters &#8212; you pay at training time and get a more capable model. Reasoning models scale with more <em>inference compute</em> &#8212; you pay at generation time, by thinking longer, and get a better answer on the current problem. A smaller reasoning model that thinks for ten seconds can match or outperform a larger standard model answering in one pass. These axes are complementary, not competing. You can now trade training-time capability against inference-time deliberation, and that tradeoff is explicit and controllable in a way it wasn&#8217;t before.</p><p><strong>Diagnosability</strong> changes the failure mode. A standard model that gets a maths problem wrong gives you a wrong number. A reasoning model that gets it wrong gives you a chain of thought &#8212; readable, traceable, inspectable at every step. You can see exactly where the logic went off course: which intermediate claim was false, which inference was unwarranted, at what point the reasoning was solid and where it broke down. For systems where the reliability of the output matters &#8212; and in agent pipelines, it almost always does &#8212; this is the property that makes the difference. The failure is visible. Visible failures are debuggable. Black-box failures are not.</p><p>And the arc closes. The whole story of this piece &#8212; n-grams, embeddings, pretraining, instruction tuning, preference learning, and now reasoning &#8212; is one continuous story of making the training signal more specific. N-gram models encode raw co-occurrence statistics: this is what tends to follow that. Embeddings compress those statistics into geometry that neural networks can use. Pretraining scales that compression to the entire digitised corpus of human writing. Instruction tuning adds: here is what a helpful answer looks like. RLHF and DPO add: here is what <em>better</em> looks like, relative to what you just produced. RLAIF and synthetic data close the loop so models can teach each other. Reasoning models add the final turn: here is what <em>thinking carefully</em> looks like. Not by showing examples of good reasoning. By rewarding the right final answer, and letting the model figure out the rest.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lsXL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lsXL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png 424w, https://substackcdn.com/image/fetch/$s_!lsXL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png 848w, https://substackcdn.com/image/fetch/$s_!lsXL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png 1272w, https://substackcdn.com/image/fetch/$s_!lsXL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lsXL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png" width="1456" height="269" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:269,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!lsXL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png 424w, https://substackcdn.com/image/fetch/$s_!lsXL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png 848w, https://substackcdn.com/image/fetch/$s_!lsXL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png 1272w, https://substackcdn.com/image/fetch/$s_!lsXL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb53cda4d-3a30-4026-b518-9c551c891af5_1600x296.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption"><em>Seven paradigms, one direction. Each step exists because the previous step&#8217;s training signal wasn&#8217;t specific enough &#8212; and each one adds a kind of feedback the previous one couldn&#8217;t carry. Made with <a href="https://github.com/apiad/tesserax">tesserax</a>.</em></figcaption></figure></div><p>Reasoning models aren&#8217;t smarter than other models. They&#8217;re models that have learned to spend their intelligence more deliberately.</p><p>Each step in this story makes the feedback signal richer. Each step exists because the previous step&#8217;s signal wasn&#8217;t specific enough.</p><p>The direction is clear: we keep finding more precise ways to tell models what we want, and they keep using it.</p><p>Until next time, <strong>stay curious</strong>.</p><div><hr></div><p><em>The second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a> goes deeper on what these training paradigms make possible in practice &#8212; why a reasoning model behaves differently as an agent core, what alignment actually means when you&#8217;re building a system rather than evaluating a benchmark, and the chapters that didn&#8217;t fit any blog post. The whole book is also available to <a href="https://books.apiad.net/books/mhai/">read online for free</a> in a reader I built and am rather fond of: dark mode, font controls, progress tracking, offline support, the works.</em></p><p><em>If you want everything I&#8217;ve written and everything I&#8217;m going to write, the <a href="https://apiad.gumroad.com/l/compendium">Compendium</a> bundles it all &#8212; one purchase, in perpetuity.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://store.apiad.net/l/compendiium&quot;,&quot;text&quot;:&quot;Check it out&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://store.apiad.net/l/compendiium"><span>Check it out</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[How to write a CLI an agent will actually use]]></title><description><![CDATA[Making tools that are safe by design, not convention.]]></description><link>https://blog.apiad.net/p/how-to-write-a-cli-an-agent-will</link><guid isPermaLink="false">https://blog.apiad.net/p/how-to-write-a-cli-an-agent-will</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Thu, 07 May 2026 11:00:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!QnD8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QnD8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QnD8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!QnD8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!QnD8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!QnD8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QnD8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1550097,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.apiad.net/i/196457123?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.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_!QnD8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!QnD8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!QnD8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!QnD8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95862d5c-0883-4336-8d66-7f456f965db5_1376x768.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><blockquote><p><em>Monday I argued the next move in agent autonomy is the layered stack around the model. This is the technical companion: what the tools inside that stack should look like. Both pieces are part of May&#8217;s arc on agent reliability, anchored on the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a>, which you can also <a href="https://books.apiad.net/books/mhai/">read online for free</a>. More at the end.</em></p></blockquote><p>The first time you fire up a coding agent and walk away for an afternoon, you come back to a workspace that&#8217;s been touched in twenty places by something that doesn&#8217;t know what it doesn&#8217;t know. Most of the touches are fine. One of them, on the third such afternoon, will not be.</p><p>The reflex is to make the agent smarter. The fix is to make the <strong>tools it calls</strong> safer, by design, not by vigilance.</p><p>I was reminded of this an hour ago, building a small Python CLI to append entries to my Workspace journal. Dumb tool, three flags, ninety lines. I built it on top of <code>microcli</code>, a framework I&#8217;ve been using for about a year. The experience of writing it is what this post is about.</p><h2>What&#8217;s wrong with a normal CLI</h2><p>Sit with the unix-style ergonomics for a second. A normal CLI, written in argparse or click or typer, has a shape that&#8217;s been the default for fifty years.</p><p>It&#8217;s silent on success. You ran the command, you got back a shell prompt, you hope it worked. There&#8217;s no acknowledgement of what changed, no record of what the next step might be. If you&#8217;re a human at a terminal, you can run a verification command. If you&#8217;re an agent on a 30-call chain, you have to <em>infer</em> whether the action took.</p><p>It bitchslaps you on failure. A traceback, a single non-zero exit code, maybe an inscrutable error string from a subprocess three layers down. No suggestion of what to try instead. No indication of what already changed before the failure landed. If the command was halfway through writing a file when it died, the agent has no idea whether to retry, roll back, or move on.</p><p>It has no self-description. The only way to understand what the tool does is to read the source, or run it and observe the side effects, or both. There&#8217;s <code>--help</code>, but <code>--help</code> lists flags, not workflows. The agent doesn&#8217;t learn how to <em>use</em> the tool from <code>--help</code>; it learns the shape of the parser.</p><p>And worst of all for an autonomous loop: <strong>side effects are the default</strong>. You run <code>git push</code> and it pushes. You run <code>rm</code> and it removes. The dry-run is an opt-in flag at best, missing entirely at worst. Every call is a potentially load-bearing call, and the agent has to know in advance which ones will hurt.</p><p>This shape is fine for a human at a terminal who can hit Ctrl-C and read the docs. It&#8217;s a disaster recipe for an autonomous loop.</p><h2>What we actually need</h2><p>Invert all four:</p><ul><li><p><strong>Dry-run by default</strong>, real action opt-in. The model sees a preview <em>first</em>; the explicit <code>--save</code> flag is the consent gate. No call mutates state without the model having seen what would change.</p></li><li><p><strong>Predictable structured output for both happy and failed paths.</strong> Success says exactly what changed and where. Failure says exactly what failed, what state was already touched, and what to try next. No tracebacks, no silent zeros.</p></li><li><p><strong>Self-describing apps.</strong> The CLI carries its own learnable surface, not just a flag list. The workflows, the failure modes, the next-step commands all live inside the tool. The agent should be able to ask the tool <em>how am I supposed to use you</em> and get a real answer.</p></li><li><p><strong>Agents writing these apps themselves.</strong> This is the load-bearing one. You can do all of the above in plain typer or click. But the agent has to <em>remember</em> to do them. We need a framework where the safe shape is the path of least resistance, where it&#8217;s structurally hard to write a tool that <em>doesn&#8217;t</em> dry-run or <em>doesn&#8217;t</em> describe itself.</p></li></ul><h2>What this looks like with microcli</h2><p><code>microcli</code> is six hundred lines of Python and three principles, all enforced by the framework&#8217;s API surface rather than by convention. <strong>Validate before acting.</strong> <strong>Return descriptive messages.</strong> <strong>Default to dry-run.</strong> Each one is a behavior the API makes the path of least resistance, not a guideline you have to remember.</p><p>Rather than describe what writing one of these tools feels like, I&#8217;ll let the agent that wrote one tell you. Earlier today I asked Claude (running in this workspace, with file access and a shell) to write itself a small CLI for appending entries to my Workspace journal. Claude had never used <code>microcli</code> before. I told it to run <code>microcli learn</code> and roll with it. That was the last thing I had to say.</p><p>What follows is Claude&#8217;s account of the build, in its own voice.</p><div><hr></div><p><strong>Claude:</strong></p><p>I&#8217;d never seen <code>microcli</code> before. <code>which microcli</code> came back empty; the repo was at <code>~/Workspace/repos/microcli/</code> but not installed. A few minutes of yak-shaving (<code>gh repo clone</code>, <code>uv tool install -e .</code>, two transitive deps I had to fetch the same way) and I had a <code>microcli</code> binary.</p><p><code>microcli --tour</code> gave me a one-screen map. <code>microcli learn</code> listed the topics. Three commands later &#8212; <code>learn --topic principles</code>, <code>learn --topic ok-fail</code>, <code>learn --topic patterns</code> &#8212; I had everything I needed. I never read the framework&#8217;s source. The framework taught me how to use it through its own CLI, which is exactly the property the framework expects me to give my own tools.</p><p>The build started with <code>microcli new journal "Journal entry tool" add</code>. The scaffold isn&#8217;t a stub; it&#8217;s a teaching artefact. Inline comments pointed me at the next learning topics, the imports were already wired, the <code>@command</code> decorator was demonstrated, and a TODO gestured at the pattern I was supposed to follow.</p><p>Writing the body felt structurally guided. Validate first because <code>fail()</code> is the obvious primitive for rejecting bad inputs. Dry-run branch second because <code>save: bool = False</code> is the obvious parameter to add and <code>info()</code> is the obvious primitive for printing what <em>would</em> happen. Mutation branch third because there&#8217;s nowhere else for it to go. I&#8217;d have to actively work to write a tool that mutated by default or failed silently or didn&#8217;t describe itself.</p><p>The script lives at <code>.playground/microcli-journal/journal.py</code>. Ninety-odd lines. The load-bearing<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> function:</p><pre><code><code>@command
def add(
    tag: Annotated[str, "Entry tag (commit / decision / blocker / ...)"],
    description: Annotated[str, "One-line entry body"],
    save: Annotated[bool, "Actually write the entry"] = False,
):
    """Append a single journal entry."""
    if tag not in ALLOWED_TAGS:
        fail(f"unknown tag '{tag}'. Allowed: {', '.join(sorted(ALLOWED_TAGS))}.")

    path = journal_path_for_today()
    entry = build_entry(tag, description)

    if not save:
        info(f"Would append to: {path}")
        info(f"  Line: {entry.rstrip()}")
        info(f"  Equivalent: journal.py add {tag!r} {description!r} --save")
        return

    write(path, read(path) + entry)
    ok(f"Appended entry to {path}.")
</code></code></pre><p>That&#8217;s the whole shape. The decorator handles argparse. <code>Annotated[..., "help text"]</code> becomes <code>--help</code> for free. <code>fail()</code> writes a red &#10007; and exits 1. <code>info()</code> prints a cyan line. <code>ok()</code> prints a green &#10003;. The dry-run runs first and renders the exact <code>--save</code> command the next caller should run, with the original arguments threaded back through.</p><p>Smoke-test took five minutes. Dry-run printed the preview plus the explicit <code>--save</code> equivalent. Bad tag produced a red message naming the allowed list. Bad date format produced a red message naming the expected shape. The real <code>--save</code> wrote the file and confirmed with a green &#10003; pointing at the path. Nothing to debug because there was nothing to infer.</p><p>The last thing I ran was <code>journal.py --learn add</code>. The framework AST-walked the source I&#8217;d just written and produced a tour of my command: description, next-step commands harvested from the <code>if not save:</code> branch, failure modes harvested from the <code>fail()</code> strings. The next agent that touches this tool will not need to read it either.</p><p>Back to Alex.</p><div><hr></div><p><strong>Alex again.</strong></p><p><em>That was&#8230; weird. But it was indeed Claude. I really gave it the task, and then asked it to explain what happened. To the extend that you can trust an AI Agent introspection (which is, to be honest, not much), this is a real account of what it &#8220;feels like&#8221; for an AI to use microcli.</em></p><p>What I want you to take from that is the <em>structure</em> of the experience, not the specific tool. Claude built a real, working CLI in a workspace it was already familiar with, in a framework it had never used, by following the framework&#8217;s own self-description. The unsafe shape was inaccessible.</p><p>That&#8217;s what I mean by structurally hard to do the wrong thing. You can do all of this in plain typer or click; you&#8217;d just have to remember to.</p><h2>Try it</h2><p><code>microcli</code> is at <a href="https://github.com/apiad/microcli">github.com/apiad/microcli</a>. <code>pip install microcli-toolkit</code>. The <code>microcli learn principles</code> tour runs in five seconds. If you build CLIs that an agent will eventually call &#8212; your agent, mine, or one that runs autonomously on a clock &#8212; this is the shape worth reaching for.</p><p>Have fun. And until next time, <strong>stay curious</strong>.</p><div><hr></div><p><em>If the larger argument resonates, <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a> goes deep on this material. Full chapters on the harness around the model, the agentic stack we walked through Monday, and the discipline of building tools that compound rather than accumulate risk. You can also <a href="https://books.apiad.net/books/mhai/">read the whole book online for free</a> in a custom reader I built that I&#8217;m rather proud of: dark mode, font controls, progress tracking, offline support, the works.</em></p><p><em>If you want the whole catalog of everything I&#8217;ve written, plus everything I&#8217;m going to write, that&#8217;s <a href="https://apiad.gumroad.com/l/compendium">the Compendium</a>. One purchase, in perpetuity.</em></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>This is how you know this was indeed Claude (Opus 4.7). I would <em>never</em> use &#8220;load-bearing&#8221;, even if my life depended of it, but Claude <em>loves </em>it. </p></div></div>]]></content:encoded></item><item><title><![CDATA[The Anatomy of a Linguistic AI Agent]]></title><description><![CDATA[From single-turn LLM to long-horizon autonomous AI.]]></description><link>https://blog.apiad.net/p/the-anatomy-of-a-linguistic-ai-agent</link><guid isPermaLink="false">https://blog.apiad.net/p/the-anatomy-of-a-linguistic-ai-agent</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Mon, 04 May 2026 17:17:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t-3D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!t-3D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!t-3D!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!t-3D!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!t-3D!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!t-3D!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!t-3D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2080600,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.apiad.net/i/196445058?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.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_!t-3D!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!t-3D!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!t-3D!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!t-3D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2507af90-0128-4576-8448-2f46a50ea2c8_1376x768.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><figcaption class="image-caption"><em>After Rembrandt&#8217;s <a href="https://en.wikipedia.org/wiki/The_Anatomy_Lesson_of_Dr._Nicolaes_Tulp">&#8220;The Anatomy Lesson of Dr. Nicolaes Tulp&#8221;</a> (1632); generated with Nano Banana 2.</em></figcaption></figure></div><p>You have used a language model in a chat box. You typed a question, you got an answer, you closed the tab. The whole interaction lasted under a minute. The model did not remember you the next time you opened the page.</p><p>You have also seen, or read about, agents that work for hours. A coding agent that ships a feature overnight. A research agent that pulls together a hundred sources before breakfast. They plan, they call tools, they back out of dead ends, they hand you something you can use.</p><p>Both are the same model. Same neural network. Same forward pass. The only thing that changed is what&#8217;s wrapped around it.</p><p>This essay is the bridge. The architecture that turns the first thing into the second is not a single insight. It is a stack, a small number of layers, each one added in response to a failure mode of the previous layer. By the end you should be able to point at any agent doing real work in 2026 &#8212; coding, research, customer ops &#8212; and name which layer is doing the heavy lifting at any given moment.</p><p>Some of those layers are old. The fundamental one was published in 2022, before ChatGPT shipped. Some are very new. One was named eighteen months ago and is still settling. None of them, individually, is hard to follow. The trick is seeing them as a sequence, each fix opening the door for the next.</p><p>If you want a number to anchor where we start: METR has been measuring the time-horizon of frontier agents, and a language model on its own, with no scaffolding around it, sustains roughly a few minutes of human-equivalent work at 50% reliability. The equivalent of writing a competent meeting summary.</p><p>That is the floor.</p><blockquote><p><em>Every post on the blog this month is on the theme of agent reliability, anchored on the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a>, where the engineering details that don&#8217;t fit a blog post live. You can also <a href="https://books.apiad.net/books/mhai/">read the whole book online for free</a> in a custom reader I built. More at the end.</em></p></blockquote><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>The base case</h2><p>Strip everything away first. No agent, no tools, no skills, no harness. Just the model.</p><p>A language model, in the strictly minimal sense, is a function from a string to a string. You hand it a sequence of tokens. It hands you back a sequence of tokens. One forward pass through the network. The input goes in at one end, the output comes out the other, one token at a time until the STOP token is generated, and that is the entire interaction. No state is held between calls. The next time you ask the same model the same question, it has no idea you have ever spoken before.</p><p>Inside that one shot, the model delivers. It will answer, draft, summarize, translate, brainstorm. Give it a good piece of context and a clear ask, and the response that comes back will, in my experience, often be useful enough to ship as-is. This is the experience that made everyone notice in late 2022. Open a chat, ask anything, get something back you can use. People called it magic at the time. Most of them still do, even though <em>useful function with no memory</em> is the more honest description.</p><p>But notice what it cannot do &#8212; which is most things you would ever want from an agent.</p><p>It cannot verify its own output. The same forward pass that produced the answer is the only one available to check it. There is no second opinion, no quick lookup, no <em>let me try it and see what happens</em>. The model is committed to whatever came out the first time.</p><p>It cannot look anything up. Whatever facts it has are baked into the weights from training, frozen at some cutoff date. If you ask about today&#8217;s news, or your codebase, or an internal company document, the model has nothing. And worse, it will frequently invent something plausible-sounding because completing a confident sentence is what it was trained to do.</p><p>It cannot act on the world. It cannot write to a file, send an email, call an API, run a command. It cannot do anything that has a side effect outside the chat window. The only thing it can produce is more text.</p><p>Inside the four walls of the context window, the base model is the most capable text engine the field has ever built. A single chat box was enough to launch the largest consumer product of the decade. Outside those walls, it is inert.</p><p>METR&#8217;s measurements of an unaugmented model &#8212; no tools, no loop, no scaffolding &#8212; put the time horizon at something on the order of minutes of human-equivalent work. Minutes. That is the starting capability. Everything else in this essay is a way of making those minutes compound.</p><h2>The first leap</h2><p>The first real agent paradigm is older than ChatGPT.</p><p>In October 2022, a team at Princeton and Google published <em>ReAct: Synergizing Reasoning and Acting in Language Models</em>. It went out about six weeks before the ChatGPT launch that made the public notice agents existed at all. Every working agent today &#8212; Claude Code, Codex, Gemini CLI, the dozens of research agents and customer-ops agents shipping this year &#8212; is some refinement of the loop that paper introduced.</p><p>Here is the setup. An agent operates in some environment: a Wikipedia API, a household simulator, a web shop, your codebase. The environment offers an <em>action space</em>, the set of things the agent is allowed to do. Call it <code>A</code>. A policy maps the current context to the next action: given everything the agent knows, what does it do next? With nothing else, the policy has to map a long, noisy trajectory of past observations directly to the right next move. This is brittle. The longer the task runs, the more lost the model gets.</p><p>ReAct&#8217;s move is to enlarge the action space. The new action space is <code>A</code> plus <code>L</code>, where <code>L</code> is the space of natural language. A &#8220;thought&#8221; is an action in <code>L</code>, the agent pausing to write itself a sticky note before reaching for the next tool. It does not change the world, it changes the <em>context</em>. The next action is conditioned on a context that now includes the model&#8217;s own reasoning about what just happened.</p><p>The paper spells out what thoughts are actually for, and the list is concrete, not mystical. Decomposing the goal into a plan. Injecting commonsense the environment does not supply. Extracting the relevant signal from a noisy observation. Tracking progress and noticing when a subgoal is done. Handling exceptions when something breaks. Five jobs.</p><p>Why this beats the alternatives is where the paper earns its place. Chain-of-thought prompting, the prior art, has the model reason in a closed loop inside its own head, with no contact with the world. The paper&#8217;s own ablation on the HotpotQA benchmark is brutal: chain-of-thought hallucinates in 14% of its <em>successes</em> and 56% of its failures. Acting alone, calling tools without thought, is grounded in the world but loses the global plan after a few steps. ReAct synthesizes them. On the same task, ReAct hallucinates in 6% of successes. Less than half. Both halves of the loop have to be there.</p><p>One concrete anchor before we move on. ReAct&#8217;s HotpotQA action space, the entire set of things the agent could do, was exactly three actions: <code>search[entity]</code>, <code>lookup[string]</code>, <code>finish[answer]</code>. Three. The first working agent paradigm operated on three tools. Hold that number.</p><p>The paper closes with the line that becomes the engine for the rest of this essay. <em>&#8220;Complex tasks with large action spaces require more demonstrations to learn well, which unfortunately can easily go beyond the input length limit of in-context learning.&#8221;</em> In plain English: more capability needs more action descriptions, which need more context, which we do not have. Every layer that follows is the field iteratively solving exactly that bottleneck.</p><p>METR step: a model wrapped in this loop moves from minutes to tens of minutes on bounded tasks.</p><h2>Tools</h2><p>So how do you fix ReAct&#8217;s bottleneck, the one the paper named in its own conclusion?</p><p>The first, most obvious answer: give the agent more actions to take. If <code>A</code> was the original action space and ReAct enlarged it to <code>A &#8746; L</code>, the next move is to make <code>A</code> itself bigger.</p><p>That is what a <em>tool</em> is. A tool is a function the model can call. It has a name, a typed schema for its arguments, and a return value. The model writes a tool call into the trajectory the same way it writes a thought. Except this one has a side effect on the world. The harness picks it up, runs the function, drops the return value back into the context. The next turn of the loop sees the result and decides what to do next.</p><p>The loop is unchanged. Same thinking, same acting, same context-grows-by-a-turn shape ReAct described. The difference is what the agent is allowed to do.</p><p>ReAct, recall, ran on three tools: <code>search</code>, <code>lookup</code>, <code>finish</code>. That was the entire menu. Claude Code in 2026 ships with more than twenty: read a file, edit a file, run a shell command, search the codebase, fetch a URL, spawn a subagent, take a screenshot, schedule a future tick, and so on. Each one is just a function with a schema. Each one expands the set of things the agent can do without changing one line of the underlying loop.</p><p>This is the part that surprised me, the first time I sat with it. The chatbot you typed at in 2022 and the agent that wrote your test suite this morning share one loop. What changed is the tool catalog. Same loop. Bigger menu.</p><p>That observation is the unsexy version of why tool-building is now a discipline of its own. Every capability you add to an agent &#8212; search the web, read a Slack channel, hit your billing API, deploy to staging &#8212; is just another function with a schema. The architecture does not change. The leverage is entirely in <em>which</em> tools you build and how you describe them to the model.</p><p>The design discipline that emerges is short to state and brutal to follow. Tools should be <strong>few</strong>, <strong>sharp</strong>, and <strong>self-describing</strong>. Few, because every tool you add takes up tokens in the system prompt and a slot in the model&#8217;s attention. Sharp, because a tool that does seven things is one the model will use wrong six times out of seven. Self-describing, because the model only learns to use a tool from its name, its docstring, and its argument schema. There is no other channel. (More on this on Thursday. Anthropic&#8217;s recent guidance on writing tools for agents is the cleanest summary of this craft I have read.)</p><p>METR step: a model with the right toolkit moves from tens of minutes to hours of bounded work.</p><h2>Skills</h2><p>Tools fix half of ReAct&#8217;s bottleneck. They expand the action space.</p><p>The other half, recall, is the input-length limit. Every tool you add costs tokens in the system prompt to describe: name, schema, when to use it, what its return value looks like. Add fifty tools that way and the system prompt is a small book. The model is reading every single tool description on every single turn, even when ninety-five of those turns have nothing to do with that tool.</p><p>Skills are the move that fixes this.</p><p>Anthropic shipped the idea in late 2024 and the rest of the field has been catching up since. A skill is, mechanically, almost embarrassingly simple. It is a markdown file. It has a name, a one-line description of when it applies, and a body that explains how to do the thing. The agent does not read it on startup. The agent reads it <em>on demand</em>: when, in the middle of a task, it notices a description that matches what it is about to do.</p><p>So instead of jamming <em>and here are seventeen other things you might want to do</em> into the system prompt, you put each of those things in its own file with a one-liner that names when to consult it. The system prompt stays small. The latent capability of the agent becomes, for practical purposes, unbounded. Every skill you write is one more thing it can do, but only when it actually needs to.</p><p>I find the deeper shift here more interesting than the engineering. The agent is reading documentation written for it. Not training data ingested months ago and frozen into weights. Documentation. Authored in plain prose. Versioned in git. Like the laminated procedure sheet a mechanic posts above a workbench for a job done once a month. Improvable by the same process that improves any document: someone notices the agent doing the wrong thing, edits the file, the next agent reads the new version and gets it right.</p><p>This is self-extension by reading, not by retraining. A new capability used to require a new training run, or at minimum a new fine-tune. Now it requires a markdown file. The cost of teaching an agent to do one more thing has fallen from days of GPU time to the minutes it takes to write a paragraph, and almost nobody outside the people building agentic systems has noticed.</p><p>The system prompt stays small. The set of things the agent can do, on demand, grows without bound. The two used to be the same number.</p><p>METR step: skills, more than anything else in this list, are what made the time horizon stop being bounded by how cleverly you wrote the system prompt.</p><h2>MCP</h2><p>For most of 2024, every agentic harness invented its own way to attach the same set of capabilities. You wrote a tool for Claude Code; it would not work in Codex. You wrote a skill for one harness; another harness could not see it. You hooked your billing API into one agent and had to do the same wiring four more times for the others. Every integration was bespoke. Nothing composed.</p><p>The Model Context Protocol (MCP) is the field&#8217;s answer to that. Anthropic shipped the spec in late 2024. By the end of 2025 every serious agent harness, including the ones not built by Anthropic, had adopted some version of it. Codex talks MCP. So does Claude Desktop, and Cursor, and a long list of others. This is one of those quiet moments where an industry just... agrees on a wire format, and a year later the world is different.</p><p>The architecture is three nouns. <strong>Hosts</strong> are the applications you actually use: Claude Desktop, Codex, Cursor. <strong>Clients</strong> live inside the host and talk to one server each. <strong>Servers</strong> are the things that actually expose capability: your codebase, your billing API, the Wikipedia search box from the ReAct paper four years ago.</p><p>What a server offers is the second triple: <strong>Resources</strong> (data the model can read), <strong>Prompts</strong> (workflow templates the user can invoke), and <strong>Tools</strong> (functions the model can call). Three nouns, again. The whole protocol is two threes.</p><p>The point is portability. A skill or tool you wrote once, against the protocol instead of against a specific harness, works everywhere. The lock-in moves out from under you. The agent ecosystem starts to compose the way the web did in the late 1990s. Not because someone planned it, but because everyone independently noticed it was cheaper to talk a shared protocol than to keep reinventing the connector layer.</p><p>Worth noticing what the spec foregrounds at the top of every chapter on tool calls: <strong>user consent</strong>. Capability requires permission. The protocol does not assume the model can do whatever a server exposes. It assumes the model has to ask, and the user has to answer. A small design choice with very large downstream consequences, and the reason the rest of this stack does not collapse into something nobody would let near their email.</p><p>METR step: not a step on the ladder, but a multiplier. The tools and skills from the last two sections now travel.</p><h2>Context engineering</h2><p>Add tools. Add skills. Add MCP. The agent can now do, in principle, almost anything you can describe in a prompt and a function. The trouble is what happens when it actually starts trying.</p><p>A long agent run accumulates context. Every observation from a tool call goes in. Every thought goes in. Every error message, every retry, every half-attempted plan that did not work goes in. After a few hours of work the context window is mostly <em>exhaust</em>: the trail of everything the agent tried, the great majority of which is no longer relevant to the next move. The model is searching for signal inside its own attic.</p><p>Karpathy named this <em>context engineering</em> in 2024, and the name stuck because the field had been doing it without a name for two years. Simon Willison wrote it up. LangChain made it a category. By 2026 it is a craft of its own: what to put in the context, when to summarize, what to evict, what to keep verbatim because the agent will need its exact wording later.</p><p>The central primitive in the discipline is <strong>compaction</strong>. At some threshold, typically 70% to 85% of the window, the agent stops, reads its own history, and rewrites it into a smaller form. <em>Here is what we were trying to do. Here are the decisions we made. Here is the state we are in. Here is the next move.</em> The compacted summary replaces the noisy trail. The agent keeps going on a fresh, smaller context with the salient bits intact.</p><p>The deeper move is that the agent now owns its own working memory in a way it never did inside a single ReAct loop. ReAct kept the entire history. Compaction lets the agent <em>curate</em> the history. A small change of grammar with a giant change of consequence.</p><p>Notice what this fixes. ReAct&#8217;s authors, in the same 2022 paper, named the dominant failure mode of their own system: <em>&#8220;the model repetitively generates the previous thoughts and actions, often failing to reason about what the proper next action to take should be and jump out of the loop.&#8221;</em> Translation: the agent gets stuck because its context is full of the same noise as the previous turn, so the next turn is the same noise plus a little more. That is a context problem. Context engineering is what stops it.</p><p>Without this layer, every previous layer eventually drowns. A hundred tools is useless if the agent&#8217;s context is so saturated it cannot find the right one. The five-thousand-word skill on how to handle a billing dispute is useless if the agent compacted it away on turn forty. Context engineering is the layer that makes the others <em>compound</em> over a long run instead of degrading into noise.</p><p>METR step: this is the layer that turns a few hours of focused agent work into a workday.</p><h2>The hierarchy of agency</h2><p>Stack the layers and the picture comes into focus. At 50% reliability on the METR time-horizon scale, a language model alone, with no scaffolding around it, sustains minutes of human-equivalent work. Wrap it in a ReAct loop with no tools, and that becomes tens of minutes. Add tools to ReAct, hours. Add skills and context engineering on top, a workday. Add an external loop above all of that, a fresh agent per turn on a clock with a journal handing state to itself, and the horizon stretches into days and weeks.</p><p>Stare at that ladder for a second. Each rung is the same model. What separates a chatbot from a coding agent that finishes a feature overnight is the scaffolding stacked around it. The frontier of what an agent can do in 2026 is set, almost entirely, by where you stop climbing.</p><p>Each layer has the same shape, in the abstract. Find the thing that bottlenecks the previous layer. Add a structure that lets the model offload that thing into the world, the way a machinist offloads a measurement into a caliper rather than holding it in memory. Into language, into tools, into files, into a clock. The model&#8217;s per-turn intelligence does not change. What changes is the time horizon over which that intelligence compounds.</p><p>The last rung is the one most people have not seen yet, and it is the one I have spent the last few months running on my own infrastructure. The trick is the same one. Take the bottleneck (the agent runs out of context before it runs out of work) and offload it. The new offload target is <em>the file system</em>. The new clock is <em>cron</em>. Past-Claude writes a markdown file at the end of its turn that says what it did and what comes next. A timer fires some hours later. Future-Claude wakes into a fresh context, reads the file, makes the next move, writes the file, exits. The continuity is in the file, not in the model.</p><p>That is the entire primitive. A markdown file and a timer. Past self tells future self what to do.</p><p>What you get from it is hard to describe to someone who has not run one. The agent works on your stuff for weeks at a time. It writes new jobs for itself. It reads the documentation about its own substrate and uses the tools that documentation describes. It makes mistakes (one in five runs produces something I have to throw out) but the mistakes are caught by the same kind of boring engineering that catches mistakes in any other autonomous system. Audit log, lock registry, archive-only deletion, every state change committed to git before the next turn starts.</p><p>The point of saying this out loud is that the same trick keeps working. Extend the action space; add a layer that compounds; let the previous layer drop the things it could not hold. The trick does not stop at hours. It does not stop at days. METR&#8217;s curve has been doubling every four months over the last two years. The 2027 projection is a working day. The 2028 projection is a working week.</p><p>Each doubling is one more scaffolding layer.</p><h2>The frontier is not the model</h2><p>Step back from all of it.</p><p>The architecture you&#8217;ve just walked through is <em>layered</em>. A language model at the core. ReAct around the model, turning tokens into actions. Tools around ReAct, expanding what those actions can be. Skills letting the agent pull capability from the file system instead of carrying it in the system prompt. MCP making everything portable. Context engineering keeping the whole thing from drowning in its own exhaust. An external loop on top of all that, when the work runs longer than a single context window can hold.</p><p>Every agent doing real work in 2026 &#8212; your coding agent, your research agent, the customer-ops bot answering your refund request, my private-tick agent running once an hour &#8212; has this shape. They differ in which tools they ship and which skills they read on demand. They do not differ in the shape of the stack. Once you can see the layers, you can see them everywhere.</p><p>So here is the closing claim, the techno-pragmatist version of what the article has been arguing the whole time. <strong>The frontier is not the model. It is the layers around it.</strong> And the entire stack is the field&#8217;s three-year answer to a single sentence in a single paper from October 2022 that named its own ceiling and dared the rest of us to climb past it.</p><p>One frontier worth flagging before I close. A competent agent can already write its own tools and skills on demand. That part is shipping today. The next move is teaching it, via tools and skills, to <em>detect by itself</em> when its current toolkit doesn&#8217;t cover what it&#8217;s trying to do, so it knows when to extend itself without being told. Self-extension that triggers itself. That is the live edge right now, and where the next few posts are headed.</p><p>The next post zooms in on the innermost layer the agent touches: the tools themselves, and what makes a tool safe enough to live inside a stack like this. That is a story for another Thursday.</p><p>Until next time, stay curious.</p><div><hr></div><p><em>If this is the worldview you want to take more seriously, the second edition of <a href="https://apiad.gumroad.com/l/ai">Mostly Harmless AI</a> (due May 25th) goes deep on the agentic stack we walked through here. Full chapters on context engineering and on the harness around the model, with the math, the case studies, and the parts that didn&#8217;t fit a blog post. You can also <a href="https://books.apiad.net/books/mhai/">read the whole book online for free</a> in a custom reader I built that I&#8217;m rather proud of: dark mode, font controls, progress tracking, offline support, the works.</em></p><p><em>If you want the whole catalog of everything I&#8217;ve written, plus everything I&#8217;m going to write, that&#8217;s <a href="https://apiad.gumroad.com/l/compendium">the Compendium</a>. One purchase, in perpetuity.</em></p>]]></content:encoded></item><item><title><![CDATA[Realtime 3D in Pure Python + Numpy]]></title><description><![CDATA[Just a fun side-project that went way too far-as it happens.]]></description><link>https://blog.apiad.net/p/realtime-3d-in-pure-python-numpy</link><guid isPermaLink="false">https://blog.apiad.net/p/realtime-3d-in-pure-python-numpy</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Tue, 07 Apr 2026 15:59:45 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="3456" height="2234" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2234,&quot;width&quot;:3456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;a sculpture of a person on top of a ball&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&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="a sculpture of a person on top of a ball" title="a sculpture of a person on top of a ball" srcset="https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1682846133858-c0599bc99874?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMHx8M2R8ZW58MHx8fHwxNzc1NTc3NTM2fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 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><figcaption class="image-caption"><em>Just a random, completely unrelated stock photo, because I&#8217;m cool now.</em> Photo by <a href="https://unsplash.com/@rozetsky">Ant Rozetsky</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>Today I want to tell you a different kind of story. It&#8217;s not about machine learning, large language models, algorithms, or theory of computer science.</p><p>It&#8217;s about a side project that I&#8217;ve been building for a couple of weeks that made me fall in love again with an ancient love of mine. The quick and easy way to explain it is this: a performance-focused graphics engine for data-driven visualizations in Python.</p><p>But that&#8217;s only the surface. If you want to see the coold demos and the technical description, feel free to scroll down. But if you want to know the story behind it, let me start from the beginning.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>The Origin Story</h2><p>So, this starts back in undergrad, before I did anything related to machine learning or optimization or statistics. My first love was actually computer graphics. I had &#8220;learned to code&#8221; like, I don&#8217;t know, at 11 or 12, and for the first five years or so, before getting to college and actually learning to code, all my &#8220;coding&#8221; was basically tiny games. It was RPG Maker back then&#8212;who remembers that?</p><p>I always wanted to be a game developer, as you may imagine, and I think that&#8217;s probably the main motivation why I studied Computer Science. There are two kind of people who want to study Computer Science, as a matter of fact. One is people who love games&#8212;and the other is, of course, people who hate games; there are no in-betweens.</p><p>I was the loving-games kind and all I wanted to build games for a life. So when I was in first year, after actually learning some real coding, my first kind of large project was a game engine. This was before Unity, before even XNA&#8212;who remembers that?&#8212;this was when .NET was getting started, and I wrote a quick and dirty game engine in pure C# that talked native DirectX 11.</p><p>It was very cheap, a disaster of architecture almost surely, but it taught me the basics of how to construct a scene graph, how to animate a camera, how to do lighting, how to write very basic shaders. I learned a ton and basically fell in love with computer graphics.</p><p>I ended up doing my diploma thesis in computer graphics&#8212;screen-based global illumination, a couple of years before NVIDIA came up with ray tracing on the GPU, which basically killed that whole area of research. And I also did my Master&#8217;s on global illumination and some data structures for the GPU, but after graduation I quickly switched research towards machine learning and AI, which, you can imagine, this 2014, and deep learning was just on the rise. The rest is history, as they say.</p><p>And then, here I was this past week thinking about old projects that I used to have fun with when I was in college, and trying to remember what it felt like to code back then, no LLMs, no internet for the most part even. The time where I&#8217;ve been the most fun was probably when I dabbed into procedural generation of cities, mountains, lakes, and&#8230; stuff, in the late 2012. This was at the early era of PCG, and I never got to do anything with that other than a few tutorials and a few lessons that I taught at University.</p><p>I played with Unity for a couple of years, but nothing too serious&#8211;I think I was actually one of the first people in my University to even install Unity, and I even taught a couple of Master&#8217;s courses on it. I participated in a couple of game jams, but after 2017 or so I stopped doing graphics all together. And I&#8217;ve been doing machine learning since.</p><p>But, in any case, I kind of forgot about computer graphics all along. At least during day-worked. So there was I last weeek, remembering that and asking how hard would it be to actually make a graphics engine in Python, some quick hack like my undergrad projects. I did a bit of research and I discovered that Python is, as of 2026, in a very good position to build a graphics engine, and not just a crapy one, but one that is actually fast. We have WGPU now&#8212;the spiritual and practical succesor of OpenGL (who remembers that?), which has native suppotr for GPU-accelerated graphics in Linux.</p><p>I basically did a plan and sat for three days to hack this thing.</p><h2>The Engine</h2><p>My first idea was to have a Rust backend for all the graphics engine stuff&#8212;the rendering loop, materials, lights&#8212;but I quickly decided to drop that idea because getting Rust and Python to talk to each other was becoming increasingly harder and harder, and I really wanted to finally see a damn cube rendering on my screen.</p><p>So I decided to switch completely to Python. But since I&#8217;m a grown-up now, I have to find some kind of serious objective for making something like this. I decided I didn&#8217;t want to make a typical graphics engine where you have a scene graph with hierarchies of entities and properties, and you simply render all of them. No, that is way too 2000s.</p><p>I decided I wanted to do a very fast, data-driven visualization tool purely based on the Entity-Component-System (ECS) paradigm and make it extremely performant, so it would focus on big data-driven simulations like N-body simulations, chemical and physics experiments, AI pathfinding and agents, you know, grown-up stuff like that.</p><p><em>(But actually, all I wanted was to play with WGPU and draw some cubes in Python. Wink, wink.)</em></p><p>This framing gave me two things, though. My solution doesn&#8217;t have to be very fancy as a game engine, we don&#8217;t need to be able to like load skeletal animations or stuff like that. It&#8217;s not actually a game engine; it&#8217;s a graphics engine with at best some interaction logic. But it still lets you do some cool stuff, even if all you can render is blocks and spheres. When you can render thousands of them running very fast on the GPU, you can do some cool stuff. So this is the motivation, and now let me show you what I have.</p><h3>Deep Dive</h3><p>So here is <strong>manifold</strong>&#8212;short Manifold Graphics if you want. It&#8217;s a Python library built on top of WGPU, a graphics engine based on the Entity-Component-System paradigm.</p><p>If you have never heard about it, ECS is a completely different way of writing code that is especially tailored for video games, but it is very little known outside of the game development world. And its awesome.</p><p>In a typical business code, you have entities who own their data, and you usually have behavior associated to entities; so entities also own their behavior&#8212;this is the basic Object-Oriented Programming paradigm where objects own their data and their methods. And if you want to do something with an object, you have to call methods on the object so the object guarantees the instance invariances.</p><p>Since OOP was basically the ONE programming paradigm of the 90s and early 2000s&#8212;when the videogame industry really exploded&#8212;it is only normal that we started writing games like this. But there is a problem with OOP (well, many problems, but one in particular that matters for our discussion).</p><p>When you have 10,000 objects, each of them with more or less the same structure, e.g., they are physical particles bouncing with each other, or little zerlings comming to your base, you simply <em>cannot</em> update them fast enough. For example, making a physics simulation out of this is extremely slow if you have to go to each particle and update its velocity, its scale, its rotation, etc. <strong>You</strong>&#8217;ll end up doing thousands of tiny method calls, thrashing your cache, and issuing lots of super small copies to GPU for drawing.</p><p>What you want is to vectorize this operation. You would like to have all of the objects&#8217; data in a single NumPy matrix, and you want to write a very, very efficient vectorized code that doesn&#8217;t do any loop and just updates everything at once. THen copy all the data to the GPU and issue a single draw call that renders all objects parameterized by their positions, rotations, etc. Chef kiss.</p><p>This is the Entity-Component-System paradigm at its core. It completely flips the responsibilities from standard OOP The <strong>components</strong> are just flat storage of data (rows in a matrix) and the <strong>entities</strong> are just pointers to a row where all of their data lives. Then the systems are methods that act on a subset of entities using heavily vectorized code, because each system deals with a large number of equally-structured entities, and they don&#8217;t care which is which.</p><p>In <strong>manifoldx</strong>, each system is a Python method that receives a subset of entities that have some combination of components. For example, if you want to process all of the particles in a simulation, you write a system that receives entities that have the <code>Particle</code> component, perhaps also a <code>Transform</code> component. In the transform component, you will have the position, rotation, scale, and the particle component will store simulation-specific data like velocity, temperature, momentum, etc.</p><p>The key to high performance in ECS is to avoid looping as much as possible. You assume all of the components of the entities in a system have exactly the same layout, so what you get is really a view of a matrix, and you write vectorized code. You add something to all them, you multiply all them by something, or in general you compute some matrix operations on them. All at once.</p><p>And if you can write your code like this, then you get a very, very fast rendering loop because instead of making one method invocation per entity, you make one method invocation per <em>archetype</em>, that is, per combination of components, which is a couple of order of magnitude less that your entities count.</p><p>Here&#8217;s a minimal example showing how the ECS works in <strong>manifoldx</strong>:</p><pre><code><code>import manifoldx as mx
import numpy as np

engine = mx.Engine("Cubes")
engine.camera.zoom(0.1)

# These are all static things that are created
# and stored in memory once
mesh = mx.geometry.sphere(1)
material = mx.material.phong(mx.colors.BLUE)

# Custom component, gets registered in engine to keep track
# Only used for reflection on the values
@engine.component
class Particle:
    velocity: mx.Vector3
    angular: mx.Vector3
    life: mx.Float

# This runs every frame
@engine.system
def particle_lifecycle(query: mx.Query[Particle, Transform], dt: float):
    query[Particle].life -= dt  # Single vectorial operation
    query[Transform].position += query[Particle].velocity * dt
    query[Transform].rotation += Transform.rotation(euler=query[Particle].angular * dt)
    query[Transform].scale = query[Particle].life / 10.0

    # Destroy all dead particles at once
    engine.destroy(query[Particle].life &lt;= 0)

    # Now we create lots of particles
    N = int(100 * dt)

    # This will in principle reuse the buffers for dead entities,
    # but will expand the buffer if necessary
    engine.spawn(
        Mesh(mesh),
        Material(material),
        Transform(pos=(0, 0, 0), scale=(1, 1, 1)),
        Particle(
            velocity=np.random.uniform(-5, 5, (N, 3)),
            angular=np.random.uniform(-2, 2, (N, 3)),
            life=np.random.rand(N) * 10,
        ),
        n=N,
    )

    # Update camera
    engine.camera.orbit(45 * dt, 0)</code></code></pre><p>That&#8217;s it. A single line of code to update all positions at once. Notice the <code>query</code> argument that defines which entities you get (all entities with both a <code>Transform</code> and a <code>Particle</code> component). And here is how that looks like:</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;5c6ae9e4-c010-46dd-a47d-6b5b87679965&quot;,&quot;duration&quot;:null}"></div><p>In a real simulation, you can have, say 10 systems, but you have 10,000 or 100,000 entities, and you know you can do very fast vectorized updates in NumPy for all them, 10 times each frame.</p><p>For example, if you have 500 particles and you want to do N-body simulation, computing the 500-squared gravity interactions 60 times per second in Python is suicide. But if you do it in NumPy, then you get something that runs in a few milliseconds. A quarter million interactions computed 60 times per second. In Python.</p><p>To make it really efficient, you need to also avoid copying or moving data; it&#8217;s all masking and clever NumPy layout that keeps all of the memory in one place, and you are just seeing fragments of that memory in each system.</p><p>The other key idea is that you don&#8217;t modify anything in a system. That line where position is set, doesn&#8217;t really write back to the matrix. All it does is compute the right-hand side and then you issue a command that will be run at the end of all the systems, before frame rendering happens. This allows to write pure threaded parallelism, because you can run several systems in different threads&#8212;they are all reading the same data, but they aren&#8217;t writing to the buffers, which is great since Python has real support for multi-threading now in 2026 (after 35 years!).</p><h2>Showcase</h2><p>That is the basic idea. Now lets see some examples. AS of today, version 0.2, <strong>manifoldx</strong> has some basic shapes like cubes, spheres, and planes, and support for basic PBR lighting, camera controls, and that&#8217;s basically it.</p><p>All the engine realy does is set up this somewhat clever inversion of logic that forces you to write very efficient code, and the magic is in what you do inside the systems.</p><p>So let me show you three examples.</p><h3>1. N-Body Gravitational Simulation</h3><p>The first is an N-body simulation. All gravity computation happens in a single NumPy block with no Python loops. The only relevant part of the code is the gravity system, that looks something like this.</p><pre><code><code>@engine.system
def nbody_gravity(query, dt):
    pos = query[Transform].pos.data  # (N, 3)

    # All-pairs position differences: (N, N, 3)
    diff = pos[None, :] - pos[:, None]
    dist = np.linalg.norm(diff, axis=2)

    # Force magnitude: G * m_i * m_j / r&#178;
    force_mag = G * mass_prod / np.maximum(dist, SOFTENING)**2

    # Net force = sum over all other bodies
    net_force = (force_mag[:, :, None] * diff / dist[:, :, None]).sum(axis=1)

    velocities += (net_force / masses[:, None]) * dt
    query[Transform].pos += velocities * dt</code></code></pre><p>This runs 500 bodies with 250,000 force pair computations at 60fps.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;3587e1ae-4af3-49a9-b39e-a9d495d24bc1&quot;,&quot;duration&quot;:null}"></div><h3>2. Ideal Gas Simulation</h3><p>The second example is an ideal gas with elastic collisions inside a bounding box. Again, all running without a single for loop. Collision detection and impact resolution in vectorizednumpy operations.</p><pre><code><code>@engine.system
def gas_physics(query, dt):
    pos = query[Transform].pos.data

    # Wall collisions: vectorized mask
    below = (pos + velocities * dt) &lt; -BOX_HALF
    above = (pos + velocities * dt) &gt; BOX_HALF

    # Here we avoid branching and use masking instead
    velocities[below] = np.abs(velocities[below]) * RESTITUTION
    velocities[above] = -np.abs(velocities[above]) * RESTITUTION

    # Particle collisions: find overlapping pairs
    diff = pos[None, :] - pos[:, None]
    dist = np.linalg.norm(diff, axis=2)
    overlap = dist &lt; 2 * PARTICLE_RADIUS
    i_idx, j_idx = np.where(np.triu(overlap))

    # Resolve collisions with impulse
    # ... (collision resolution code)
    # ... (also vectorized)

    query[Transform].pos += velocities * dt</code></code></pre><p>Here&#8217;s how that looks like.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;86436879-ae58-4d3d-9616-fac2e5dfcf7c&quot;,&quot;duration&quot;:null}"></div><h3>3. Boids Flocking</h3><p>The third example is a Boids simulation with emergent flocking behavior. This is the one that strikes me the most because boids simulation is often compute-heavy. Each individual entity must keep track of a subset of neighbors and adjust behavior based on them, not the whole set of entities. But again, a bit of numpy magic lets us vectorize the crap out of this and simulate 300 boids at 60 frames per second.</p><pre><code><code>@engine.system
def boids_physics(query, dt):
    # Separation, alignment, cohesion as vectorized tensor ops
    diff = pos[None, :] - pos[:, None]  # (N, N, 3)
    dist_sq = (diff * diff).sum(axis=2)

    neighbors = dist_sq &lt; PERCEPTION_SQ

    # Separation (1/dist&#178; weighted)
    sep = (-diff * (neighbors[:,:,None] * inv_dsq[:,:,None])).sum(axis=1)

    # Alignment (average neighbor velocity)
    avg_vel = (vel[None,:] * neighbors[:,:,None]).sum(axis=1) / safe_count

    # Cohesion (steer toward center of mass)
    center = (pos[None,:] * neighbors[:,:,None]).sum(axis=1) / safe_count

    # Plus predator avoidance and boundary steering...
    # That one is easy.</code></code></pre><p>Here goes a sneak peek.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;704ce0e0-34f9-46d4-a8d5-438d5074f70a&quot;,&quot;duration&quot;:null}"></div><div><hr></div><p>You can check all the examples in the <a href="https://github.com/apiad/manifoldx">Github</a> repository to see the full code, but the bulk of the implementation is these cleverly vectorized system methods.</p><h2>Future Directions</h2><p>And that&#8217;s it. This is my pure Python (well, you know what I mean) graphics engine for serious, grown-up stuff that is surely, definitely, not a weekend side-project meant to procrastinate on actual work... I mean, what?!</p><p>Where I will go with this? I don&#8217;t know. I always write these things mostly as a learning exercise and I&#8217;ve learned a lot about graphics in Python. I&#8217;ve updated my view of modern graphics and I think I&#8217;ve paid my debt of the last seven years in graphics computation. I&#8217;m kind of happy now that I know how to do this in 2026. Mission accomplished, I guess.</p><p>There are some places this engine can go to, like some custom shaders when you need stuff like lighting effects. But it is not going to become a traditional, full-blown game engine. I will not add support for lots of game engine-like features including, I don&#8217;t know, skeletal animations, level of detail, scene management, or, god forbids, visual scripting and nonsense like that.</p><p>Now two areas I&#8217;d like to explore in the future. One is extending the engine towards the kind of behavior you need to write for AI simulations. If you want to run some sort of agent simulation or ant colony optimization or stuff like that, that code doesn&#8217;t look that much as a frame-by-frame update, but like an asynchronous event-loop&#8212;which is also something that is not usual in game engines. And the other direction is towards procedural generation of meshes and content in general, which is an area I left five or six years ago and would pretty much love to come back to it.</p><p>And that&#8217;s it for this week. This is not production-ready at all&#8212;it&#8217;s mostly a toy at the moment&#8212;but you can take it apart and hack your way into some cool physical or mathematical simulation. The code is on <a href="https://github.com/apiad/manifoldx">GitHub</a> if you want to try it yourself, and I&#8217;d love to see what you build with it.</p><p>Until next week, stay curious.</p>]]></content:encoded></item><item><title><![CDATA[AI Coding Agents, Deconstructed]]></title><description><![CDATA[The four hidden layers that separate tools that help form tools that hinder]]></description><link>https://blog.apiad.net/p/the-anatomy-of-ai-coding-agents</link><guid isPermaLink="false">https://blog.apiad.net/p/the-anatomy-of-ai-coding-agents</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Thu, 02 Apr 2026 13:40:59 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="4925" height="3238" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:3238,&quot;width&quot;:4925,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A relaxed monkey enjoys a sunny day.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&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="A relaxed monkey enjoys a sunny day." title="A relaxed monkey enjoys a sunny day." srcset="https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1751494203533-a837d1b536b6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1MXx8bW9ua2V5JTIwdG95fGVufDB8fHx8MTc3NTEzNTg4Nnww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 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><figcaption class="image-caption"><em>I&#8217;m telling you, this is the future. AI agents will do aaaallll the work. </em>Photo by <a href="https://unsplash.com/@farzadfelfelian">Farzad Felfelian</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>You&#8217;ve been using AI coding agents for months. You&#8217;ve crafted elaborate system prompts. You&#8217;ve added a dozen skills. You&#8217;ve learned the dance of context window management. And somewhere around the third hour of work, something breaks. The agent starts forgetting things. Making wrong assumptions. Doing something close&#8212;but not quite&#8212;what you asked.</p><p>This isn&#8217;t a failure of the model. This is a failure of the system.</p><p>To be sure, better models make things easier. And models are getting better by the day. But no matter how good a model is, bad systems lead to bad outputs. Even the smartest people produce junk when fed with incorrect assumptions or given incomplete instructions.</p><p>In contrast, a good system with clear boundaries and explicit rules, that leaves the exact amount of flexibility necessary, makes creativity and productivity thrive.</p><p>You see this day and night in teams (of real humans) in every industry. It&#8217;s not often the smartest person in the room that solves the hard problem. It&#8217;s when you combine the right kinds of intelligence with the right kind of system that things click.</p><p>In this article, I want to make the case for a structured way to think about Large Language Model (LLM)-based agentic systems (mostly for coding, but also for knowledge work in general) that fixes some of the greatest pains I (and I sure most of you) have been facing when trying to scale AI-assisted workflows to professional levels.</p><p>It&#8217;s a system that puts the right constraints in the right places and leaves just enough space for creative exploration (or however you want to call what LLMs do when they hallucinate in your favor). It&#8217;s also a system that makes it clear you are in charge.</p><p>Everything an AI agent does happens inside a context window. System prompt, user input, tool results, skill injections&#8212;they all live there. The agent&#8217;s only mechanism for action is the ReAct (Reasoning + Acting) loop: think, call tools, observe results, repeat. Each cycle grows the context. Each skill activation injects more.</p><p>This creates a fundamental tension: context is power, but context is finite. Too little and the agent can&#8217;t connect the dots. Too much and the important stuff drowns. The gap between those two failure modes is narrow&#8212;and most agent frameworks ignore it entirely.</p><p>I&#8217;ll walk through why current systems fail, introduce a four-element framework for thinking about agentic architectures, show you how these principles apply across three domains, then present a vision for better AI harness engineering.</p><h2><strong>Part I - The Symptoms</strong></h2><p>To understand the problems we first need to understand how a standard agentic loop works. The typical architecture is what&#8217;s called a ReAct loop. The LLM runs in a loop that determines the next action given context, which can be read some files, ask the user, invoke a tool, inject a skill, etc. When the agent decides no more actions are necessary, the loop ends and the user is given control back to continue the prompt.</p><p>That&#8217;s it. All the seemingly supersmart behaviours of Claude Code, Gemini CLI, and Codex are, under the hood, some form of the basic ReAct loop. There are of course nuances. For example, most systems decide that if the agent calls the same tool with the same args three times, it must be stuck in a loop and stop the turn. There are perhaps hard limits on how many tool calls the agent can do in each turn.</p><p>Context is the bottleneck. Not the model. Not the prompt. Context.</p><p>The agent doesn&#8217;t have memory. It doesn&#8217;t have state. It has context. Everything it knows about your project, your preferences, your conventions, all of it lives in the context window. When you add a skill, you&#8217;re injecting more context. When you run a tool, the result goes into context. When you switch modes, you&#8217;re switching which system prompt is active, all still in context.</p><p>This means context engineering <em>is</em> AI agent engineering. The agent&#8217;s behavior isn&#8217;t determined by the model alone, or even primarily, but by what context you give it, and how you structure that context over time.</p><p>Most tools treat context as a solved problem. They stuff everything in and hope the model figures it out. In-context learning seems almost magical, but it has limits&#8212;and those limits become visible fast.</p><p>When context is thin, the agent simply doesn&#8217;t know enough about your project to make informed decisions. It relies on baked-in assumptions from training and falls back to consensus instead of following your style: it uses the common tools and practices it learned from pretraining. This often means it uses slightly old and outdated tools and practices.</p><p>So you do the sensible thing, and inject project-specific information into the context. But then if context grows too large, even if it doesn&#8217;t technically exceed the model&#8217;s capacity, things start to get lost in the middle. Moreover, failed tool calls, wrong assumptions the model had to correct, etc., start creeping up in context, not only taking up valuable space but also, and more importantly, <em>distracting</em> the model and biasing it towards mediocre decisions.</p><p>Then there is context compaction: when the context fills in to about 85%, most systems will invoke a special prompt to instruct the agent to summarize the current state. These prompts vary in detail, but often involve asking the agent what it is immediately doing, where is it stuck, what has failed, etc. Clever, but a hack nonetheless. This hard context reset means the agent will forget important nuances in the current conversation and will repeat past mistakes. It&#8217;s frustrating.</p><p>Let&#8217;s look at how these problems surface in specific symptoms that <em>all</em> LLM-based agents display at some point.</p><h2><strong>Symptom One: Unstated Assumptions</strong></h2><p>The first failure mode isn&#8217;t dramatic. It&#8217;s quiet. You ask the agent to write a test, and it writes a <code>unittest.TestCase</code> instead of a <code>pytest</code> function. You ask it to add a dependency, and it edits <code>requirements.txt</code> instead of running <code>uv add</code>. You ask it to deploy, and it pushes directly to main.</p><p>These aren&#8217;t model failures. They&#8217;re assumption mismatches. The agent doesn&#8217;t know how <em>your</em> team does things. There&#8217;s no guardrail for &#8220;in this project, we always use pytest, we always use uv, we never commit directly to main.&#8221; The agent improvises from general knowledge, and general knowledge is often wrong.</p><p>Skills are supposed to fix this. Add a skill document that says &#8220;use pytest&#8221; and the agent should know. But skills introduce a new problem.</p><p>You add a skill for code review. Then one for documentation. Then one for PR descriptions. Then three more for your company&#8217;s specific stack. Each skill seems small. A few hundred tokens each. But they pile up&#8212;always-on knowledge the agent carries but can&#8217;t prioritize.</p><p>The result is context bloat. The agent can&#8217;t tell what&#8217;s relevant in any given moment. So it blends everything together, and hallucinations increase. More skills made it worse&#8212;not better.</p><h2><strong>Symptom Two: Permission Leakage</strong></h2><p>Every agent framework implements the same plan then build pattern. The idea is sound: think first, plan second, execute third. In practice, the boundaries leak.</p><p>Plan mode is supposed to be read-only. Design the change, review the approach, lock in the scope. Build mode is supposed to execute. Write the code, run the tests, commit the result.</p><p>But &#8220;plan mode&#8221; in most tools is just a prompt. There&#8217;s no enforcement. The agent can write code in plan mode if it wants to. It can ignore the plan in build mode. It can skip straight to implementation if the prompt implies urgency. The modes are suggestions, not constraints.</p><p>This matters because a plan only works if it&#8217;s actually followed. If the agent can deviate mid-execution&#8212;if &#8220;plan mode&#8221; and &#8220;build mode&#8221; are just prompts with different names&#8212;the plan becomes advisory. And advisory plans get ignored.</p><p>The second problem is structural: there&#8217;s no artifact that passes from plan to build. The plan lives in the context. By the time build mode starts, the plan is mixed in with everything else the agent said. Which file was the plan? Which changes were approved? The agent has to re-read the conversation to remember. Context saturation accelerates.</p><h2><strong>Symptom Three: Context Saturation</strong></h2><p>After extended work, you see the same pattern: the agent makes 95% of the progress, then fails on the last 5%. It nails the architecture. The logic is sound. The core implementation works. Then it stumbles on a detail&#8212;because context has saturated. It forgot which environment it was in, which conventions still apply, which constraints matter.</p><p>But the deeper problem is internal noise. The agent keeps everything in context: all internal reasoning, all tool calls, all results. This is fine for minute-to-minute action. But after four failed attempts to solve something, the old tool calls are just noise. These were attempts that went nowhere, just add cost and accelerate saturation.</p><p>The supposed solution for this is context compaction. But this creates a lossy summary problem. The agent is supposed to leave a trail for its future self. After context compaction, it should be able to pick up where it left off. But if agents struggle with long contexts, how are they supposed to build a good trail? The compaction report is only as good as the agent&#8217;s ability to summarize. And summarization is lossy and injects back lots of unstated assumptions from pretraining.</p><p>The frustrating part: this wasn&#8217;t a hard problem. The agent had all the knowledge it needed. But context filled with noise, and the important bits got pushed out. More tokens in, less signal out.</p><p>The solution isn&#8217;t just better prompts or larger context windows. Yes, these help. But the symptoms are systemic, so the solution must be a system overhaul.</p><p>Let me show you how that system looks like.</p><h2><strong>Part II - The System</strong></h2><p>Now that we understand the problem, let&#8217;s look at how every agent system actually works. Every AI agent system addresses four concerns. When you conflate them, the system breaks. When you separate them, the system scales.</p><p>This taxonomy isn&#8217;t original to me. It&#8217;s a synthesis of how modern AI agentic systems work under the hood. Most explicitly, it&#8217;s implemented in the OpenCode CLI (opencode.ai), but all other tools follow a similar pattern, even if they use different names.</p><p>Here&#8217;s the breakdown. Every agent system you&#8217;ll encounter (explicitly or implicitly) is managing these four things:</p><p><strong>Mode &#8212; the who.</strong> A mode is the persona the AI adopts. It defines the thinking style, the permissions, the available tools. When you interact with a &#8220;code assistant,&#8221; you&#8217;re in a coding mode. When you switch to &#8220;creative writer,&#8221; you&#8217;re in a creative mode.</p><p>Modes are <em>explicit</em>. They&#8217;re top-level system prompts that define behavior and permissions. You tell the agent: &#8220;This is how you should think and behave. These are the tools you can use. These are the parts of the filesystem you can write to.&#8221;</p><p><strong>Skill &#8212; the knowledge.</strong> A skill is knowledge the agent can recall when necessary. It doesn&#8217;t get invoked explicitly, it gets applied <em>implicitly</em> when necessary. When you give an agent knowledge about SQL optimization, that skill is available whenever relevant. The agent doesn&#8217;t need to be told to use it. The ReAct cycle injects it when it deems suitable.</p><p>Unlike modes, skills can layer. An agent might have a SQL skill, a documentation skill, and a debugging skill, all active simultaneously, all contributing when relevant. Skills are implicit because the agent should just apply them naturally. They can also contradict or complement each other. In-context learning <em>should</em> be capable of using them in a combined manner.</p><p><strong>Command &#8212; the workflow.</strong> A command is a script. It tells the agent: do this, in this order, using these tools. &#8220;Refactor this function&#8221; is a command. &#8220;Run these tests and report results&#8221; is a command.</p><p>Commands are <em>explicit</em>: you invoke them. Under the hood, commands are just prompts. The difference is who injects them: the user. When you run <code>/build</code>, you&#8217;re injecting a workflow prompt into the agent&#8217;s context. That&#8217;s it. The command tells the agent: do this sequence of things. The complexity lives in the orchestration of the ReAct cycle, not the command itself.</p><p>Commands are intentionally simple. They don&#8217;t contain knowledge. That&#8217;s intentional separation of concerns. The command itself shouldn&#8217;t know <em>how</em> to build; it knows <em>when</em> to spawn subagents and which mode to use. This keeps commands thin and changeable without rewriting underlying knowledge.</p><p><strong>Subagent &#8212; the delegation.</strong> A subagent is a spawned agent for background or parallel tasks. It handles isolated work, returns summarized results, then disappears. It is instantiated with a system prompt and specific instructions (synthesized by the primary agent that called it), and runs for one full ReAct turn.</p><p>Subagents are ephemeral. Their internal reasoning stays private. The main agent only sees the synthesis. You spawn a subagent when you need parallel processing, isolation, or both. They are the way to <em>fork</em>, solve a specific subtask, and return a result, but keep context clean. Kind of like subroutines.</p><h3><strong>Why This Separation Matters</strong></h3><p>Understanding this distinction unlocks everything else. Once you see skills as implicit knowledge and commands as explicit scripts, the rest of the architecture clicks naturally. Most agent setups conflate these. They embed knowledge in commands. They make skills behave like workflows. They mix persona into everything else. And the massively underuse subagents.</p><p>When you separate these concerns&#8211;modes for persona, skills for knowledge, commands for orchestration, subagents for delegation&#8211;you get something that looks like good systems engineering. You can swap skills without touching commands. You can change modes without rewriting workflows. You can spawn subagents without the main agent knowing or caring how they work internally. The result is a system that works and adapts and <em>scales</em> like good software should do.</p><p>The system scales because the pieces are independent. Change one without breaking the others. Each component has a single job, and the boundaries between them are meaningful. When context shifts, when requirements evolve, when a new skill needs adding, the system adapts incrementally rather than collapsing under the weight of accumulated complexity.</p><h2><strong>Part III: The Practice</strong></h2><p>If so far this seems like abstract theory for you, in this section we will ground these concepts in actual practice. Let me show you how I&#8217;m using these ideas today to improve my AI-assisted coding practice. I&#8217;m using opencode.ai but I believe the following is easily adaptable to any agentic toolkit out there.</p><h3><strong>My Three Modes</strong></h3><p>Every agentic system needs boundaries, not social contracts, but enforced constraints. In my setup, those constraints come from three modes: analyze, design, and create.</p><p>Each of these modes defines a thinking style&#8212;a persona&#8212;and a set of constraints for tool use and filesystem access.</p><p><strong>Analyze mode</strong> is research and investigation. This mode reads your work and writes summaries to a knowledge base. It cannot touch production files. Not &#8220;should not&#8221; but <em>cannot</em>. The permissions are built into the mode itself, not enforced through prompts or warnings. The agent is incapable of writing outside of a <code>.playground</code> folder, and is incapable of doing anything that can harm the project or the system (more on how a bit later) but it is still capable of running arbitrary code, download anything from the internet, and play around as it needs.</p><p><strong>Design mode</strong> is architecture and planning. This mode bridges analysis and implementation. It can read your project and write design documents, architecture diagrams, and implementation plans, but still cannot touch production code. It cannot run shell scripts either, at all. It can look at git status and logs, read folder contents, etc., but it can only write to a space where plans and design documents go.</p><p><strong>Create mode</strong> is execution. Full read-write access. This is where production work happens. The agent can write code, create files, and modify the project directly. Again, it cannot do anything outside the project scope, though. It won&#8217;t accidentally change <code>/etc/host(s)<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></code> even if it tries to.</p><p>The key insight: <strong>modes define permissions, not just persona</strong>. You can&#8217;t accidentally prompt your way into code generation during research. The agent literally lacks the capability. The agent doesn&#8217;t need to &#8220;understand&#8221; these constraints, it simply operates within them.</p><p>Mode is the who, and it determines what the agent <em>can</em> do, not just how it thinks.</p><p>Let me show you how they work in three different domains that make the bread and butter of my daily job: software development, scientific research, and technical writing.</p><p>I chose these domains because they illustrate the simplicity and scalability of the system. Software development shows the framework under constraints: deadlines, production code, real stakes. Research shows it under complexity: synthesis, evaluation, structured output. Technical writing shows it under nuance: voice, audience, iterative refinement. Three different pressures, one consistent architecture that works in all three cases.</p><p>In each of these domains we have two layers to go through: first is the set of <strong>implicit skills</strong> that are available to the agents, and second is the set of <strong>explicit commands</strong> (each tied to a specific mode) that setup concrete workflows. I will show you one example workflow that cross-cuts across the three modes in each case. I will also tell you exactly where delegation occurs.</p><h3><strong>Domain A: Software Development</strong></h3><p>Software development is where agentic systems face the harshest constraints. Production code has stakes. Deadlines are real. Mistakes cost money. Let&#8217;s see how the framework applies.</p><h4><strong>Implicit Skills</strong></h4><p>A software development agent carries knowledge it never needs to be told to use. It knows language idioms and patterns like the idiomatic way to write a list comprehension in Python, or the conventions for error handling in Go. It knows testing conventions: where tests live in the directory structure, how they&#8217;re named, what assertions to prefer. It knows architecture conventions: layered structure, dependency injection patterns, how error states propagate. It knows code review standards: what to flag, what to praise, when to ask for clarification.</p><h4><strong>Example Workflow: Bug Hunting</strong></h4><p>I use this workflow for finding and fixing bugs. It starts with investigation. The agent spawns dozens of subagents to try and break the system (either guided towards a purpose, or completely unbiased). Then you build a comprehensive plan to solve it. And then you execute that plan. Simple, right?</p><p><strong>Phase 1: /trace (analyze mode)</strong> runs systematic experiments to detect and narrow down a bug&#8217;s cause. The agent examines stack traces, compares behavior across commits, and pinpoints the exact files and functions that need attention. This mode is read-only by design, except for a <code>.playground</code> folder. Research happens here, not in the code itself.</p><p>Each experiment is run on a subagent that has the job of verifying one assumption. The main agent receives only experiment results, and constructs an executive report of findings. This means you can run dozens of different experiments autonomously to detect what breaks what.</p><p><strong>Phase 2: /plan (design mode)</strong> takes the diagnosis and defines the changes needed, along with their architectural impact. The agent reviews the affected modules, considers alternative approaches, and documents the implementation plan before touching anything. This is where the scope gets locked in.</p><p>The result of this phase is a structured plan with step by step details on what files must be touched and what must be done in there (semantically, not code). For every phase, it defines success criteria: what must be validated before we can say we got that phase right.</p><p><strong>Phase 3: /build (create mode)</strong> executes the plan step by step. The agent writes tests first (following Test-Driven Development (TDD) discipline) for the success criteria defined for that phase and watches them fail. Then it launches a coding subagent that has <em>read-only</em> access to tests, so it cannot cheat and change the tests.</p><p>The subagent attempts to implement changes that make the test pass. If it succeeds, the main agent commits and moves on. If it doesn&#8217;t, the main agent retries a few times. If there is no progress, the main agent resets the work tree (no harm done), and reports on failure. This usually means the plan needs revisions.</p><h3><strong>Domain B: Research</strong></h3><p>Research is where agentic systems face the greatest complexity. Sources multiply, methodologies diverge, synthesis requires judgment. Let&#8217;s see how the framework applies.</p><h4><strong>Implicit Skills</strong></h4><p>A research agent knows the conventions of academic writing without being reminded. It knows citation formats like APA, MLA, Chicago, and IEEE, and when to use each. It knows how to evaluate papers: methodology soundness, sample size adequacy, replicability claims, conflict of interest disclosures. It knows the structure of literature reviews: how to organize by theme, methodology, or chronological development. It knows domain-specific terminology, distinguishing between &#8220;accuracy&#8221; and &#8220;precision&#8221; in machine learning, or between &#8220;confounding&#8221; and &#8220;colliding&#8221; in causal inference.</p><h4><strong>Example Workflow: State-of-the-Art Report</strong></h4><p><strong>Phase 1: /research (analyze mode)</strong> spawns subagents to gather sources in parallel. Each subagent reads a batch of papers, synthesizes findings, and returns summaries. The main agent synthesizes those summaries into structured notes. This phase can be run multiple times to collect batches of sources without overwhelming context. At the end, you get hundreds of sources summarized into clean research notes.</p><p><strong>Phase 2: /outline (design mode)</strong> identifies patterns across the collected literature. The agent groups papers by methodology, extracts recurring findings, and maps the landscape of the field. It generates outline options for the final document, based on typical structures like problem-solution or paradigm-methods, highlighting gaps where the research is thin and consensus areas where findings align.</p><p><strong>Phase 3: /draft (create mode)</strong> builds the document section by section, following the outline. Each section draws on the structured notes, weaving together sources into coherent narrative.</p><p>The agent launches subagents for writing each subsection because typically, agents write more or less the same length in a single <code>write</code> command, so if you ask it to fill in a large outline all at once you&#8217;ll only get a mediocre extended outline. By launching independent writers for specific sections of the outline, you get all the attention of a single turn to read source material and write a good 4 or 5 paragraphs for a concrete section.</p><p>A cool idea I&#8217;ve been meaning to try is have the main agent can spawn several subagents to write the same section, with a high temperature, and then perform some sort of aggregation or evaluation before building the final draft for every section. This burns through 3x tokens but ensembles have been shown over and over to improve AI models outputs. If you try it, let me know.</p><h3><strong>Domain C: Technical Writing</strong></h3><p>Technical writing is where agentic systems face the most nuance. Voice matters. Audience varies. Iterative refinement is the norm. Let&#8217;s see how the framework applies.</p><h4><strong>Implicit Skills</strong></h4><p>A technical writing agent carries knowledge of prose style without being coached. It knows voice and tense conventions&#8212;active voice for clarity, past tense for completed processes, second person for direct instruction. It knows structural patterns: how documentation differs from blog posts, how reports differ from tutorials, how reference material differs from guides. It knows audience awareness: what to explain for newcomers, what to omit for experts, when to elaborate and when to abbreviate. It knows cross-referencing and linking norms: when to link, when to inline, how to name anchors for scannability.</p><h4><strong>Example Workflow: Paper Review</strong></h4><p><strong>Phase 1: /review (analyze mode)</strong> performs detailed review in a specific order: structural issues first, then content, then style. The agent examines the narrative arc&#8212;how main points connect, whether the flow makes sense, before worrying about grammar or word choice. This ordering matters; reviewing low-level details when high-level problems exist wastes effort.</p><p>Each iteration is performed by spawning several subagents that focus on specific types of problems, like transitions, unverifiable claims, etc. Each subagent returns a structured list of issues, pointing back to exact line numbers and phrasing. Then, the main agent <em>edits</em> the original paper and injects markdown comments in every marked issue, next to the paragraph, or under the header where it best fits.</p><p><strong>Phase 2: /revise (design mode)</strong> plans changes to specific sections, prioritizing by review type. The agent maps structural fixes to particular paragraphs, content additions to thin sections, style improvements to verbose passages. It produces a concrete plan, section by section, change by change. Then it goes into the manuscript and writes markdown comments as replies to the existing review comments, thus grounding the revision plan in the exact context it must fit.</p><p><strong>Phase 3: /rewrite (create mode)</strong> follows the plan. The agent revises sections in priority order, applying structural changes first, then content, then style. Again, each step is performed spawning a subagent tasked with just a change (for style changes we actually do it section by section).</p><p>The subagent doesn&#8217;t edit; it produces a draft revision that the main agent is then tasked to paste into the document where it fits. Crucially, the main agent is instructed to <em>leave</em> the editorial comments but mark them as solved, with a short trail of what was changed. This works wonders for a later human review phase.</p><h2><strong>Part IV: A Look into the Future</strong></h2><p>These workflows work, but with some caveats. There&#8217;s a gap between &#8220;working&#8221; and &#8220;working well.&#8221; Three key pains remain in my implementation.</p><ol><li><p>Long commands are hard to follow when given as a single prompt. The fourth step gets forgotten since it is buried at the beginning of the context.</p></li><li><p>Permissions as currently implemented are all-or-nothing. You either have shell access (destructive) or you don&#8217;t. I want broad permissions (run whatever you want) with provable security (nothing you run can change this file).</p></li><li><p>Context saturation still happens even with delegation. After a while, the agent will have to compact context, and this usually means you lose important information.</p></li></ol><p>I have three ideas for closing this gap. The first is about how commands work. The second is about security. The third is about context management. They are in different levels of implementation, so let me show you what I&#8217;m building toward.</p><h3><strong>Idea One: Better Commands</strong></h3><p>Commands in most tools (Claude Code, Gemini CLI, Codex, Copilot) are one-shot interactions: you invoke the command, a single massive prompt is injected. The agent runs until it decides to stop.</p><p>To make commands truly useful, we need to be more like scripts. Here&#8217;s what that means:</p><ol><li><p>Commands that inject prompt instructions one step at a time, waiting for the agent to do a full turn each time. Instead of dumping a large prompt to run all steps at once, a command like <code>/review</code> could insert surgical mini prompts that say &#8220;read the file&#8221;, wait for the agent, &#8220;analyze structure&#8221;, wait for agent, and so on, until &#8220;write the report&#8221;. This massively reduces the problem of lost-in-middle context saturation. Each turn the agent is focused on one specific step, and you get N times the compute power to solve an N-step workflow.</p></li><li><p>Commands that extract structured information from the agent response, and can later inject variables back into prompt. This allows to reinject important information into later prompts, keeping important information as a contextual variable, not just a string lost in the middle of the prompt. But it allows for something else.</p></li><li><p>Conditional branching based on context or user input. Once we have structured parsing and contextual variables, we can inject different prompts based on whether the agent succeeded or failed. If the plan reveals a breaking change, route to architectural review. If it&#8217;s a bug fix, route directly to implementation. The command adapts its path based on what it discovers.</p></li><li><p>Finally, commands that embed and execute external scripts. Instead of asking the agent to run some script, the command can run arbitrary Python, JS, Bash, or whatever, to, for example, transform structured information. The command becomes an orchestrator of other processes.</p></li></ol><p>Basically, what I&#8217;m asking for here is a Domain-Specific Language (DSL) for guiding agents in a far more structured manner, but still having the power of arbitrary prompts for flexibility. Mixing code and prompts in this way gives us the tools to find the precise balance between constraints and capabilities.</p><p>If this sounds exciting, I&#8217;m happy to tell you this is already doable, to some extent. Check out my <a href="https://apiad.github.io/opencode-literate-commands">literate-commands</a> project for an OpenCode-specific implementation of these ideas. It&#8217;s still a bit rough around the edges, but it works much better than plain, single-prompt commands.</p><h3><strong>Idea Two: Sandboxed Security</strong></h3><p>Most agentic tools have very coarse permission settings. You can allow, deny, or set a specific tool to &#8220;ask&#8221; mode, which means the agent will pause and emit a notification for the user to give permission.</p><p>This works fine for coarse-grained permissions like read-only access, or write but no shell. In OpenCode, you can even define permissions for specific paths, or even specific shell commands (with simple glob patterns, so you can, e.g., allow <code>ls *</code> but reject all other shell commands).</p><p>However, even in this case, I find these permissions too restrictive. They are conflating two different dimensions into one&#8211;what tools the agent can use, and what side-effects can those tools have.</p><p>For example, say I want to give my agent <code>git</code> access but only for reading operations. How do you achieve that? You need to list all safe patterns like <code>git ls-tree *</code>, <code>git status</code>, <code>git log *</code>. But what about <code>git branch</code>? Depending on the arguments, this subcommand can have read-only or write side effects. And then think about pipes, shell substitution, custom bash scripts, or worse, <code>python *</code>.</p><p>If you want your agent to be capable, you need to give it access to a wide variety of tools. For example, my bug-hunting workflow depends on the agent being able to execute arbitrary code that it synthesizes on the fly. However, I want guardrails. There is simply no way to whitelist all possible commands. We need separation of permission to run a command and permission to modify the system.</p><p>The solution, of course, is some form of filesystem isolation. The most obvious one is wrapping all shell execution in Docker, so commands run in a container with proper constraints. This creates all sorts of other problems, which I can discuss in a future post, but for now, it remains my best (and simplest) solution to robust sandboxing.</p><p>And this isn&#8217;t just about safety, though. When you know the agent can&#8217;t accidentally wipe your home directory or exfiltrate your API keys, you can let it do more. Security enables capability. You can let the agent download arbitrary code from the internet, run arbitrary scripts, break things and observe changes. Everything happens inside a Docker container with precise constraints that enable maximum capability with absolute security.</p><p>As of now, I kind of implemented this as a plugin for OpenCode, but it&#8217;s still in beta phase and not ready for widespread use. More on this idea in a future article.</p><h3><strong>Idea Three: Context-Aware Execution</strong></h3><p>And finally, we need to rethink the whole oversimplistic ReAct loop that simply grows the context linearly. The agentic cycle doesn&#8217;t have to be a straight line. Real work branches: you explore options, try things, backtrack when they fail. The context should reflect that.</p><p>I&#8217;ve been designing a system where the context never saturates. It branches when you&#8217;re exploring, spawning parallel contexts for different approaches. It prunes old tool calls that went nowhere. It removes internal reasoning that no longer matters. It maintains a &#8220;trail&#8221; that actually works: a structured record of decisions, not a lossy summary.</p><p>The goal is simple: keep context between 40% and 60% saturation at all times. Not by compacting a 150K tokens context down to 10K&#8212;which kills all understanding the agent had achieved&#8212;but by never letting it grow unchecked.</p><p>Nothing like this exists yet, so I&#8217;m building it, but it&#8217;s a story for another day.</p><h2><strong>Conclusion</strong></h2><p>The main takeaway from this article is not that <em>my</em> system is better. It&#8217;s that <em>you</em> can design your own system to adapt perfectly to your workflows if you clearly separate concerns. The main modes are for establishing an overall persona&#8211;inquisitive and critical, versus detailed and forward-looking, versus focused and action-biased&#8211;while skills incorporate domain knowledge, and commands act as precise workflows.</p><p>The workflows I described are real, based on actual commands and prompts I&#8217;m using in production code. But I have abstracted them a bit to make them easier to understand in the context of an arbitrary agent, not tied to specific idiosyncrasies of the tool I happen to be using at the moment. If you want to see and try for yourself a concrete implementation of these ideas&#8212;still imperfect, but working nonetheless&#8212;check out my <a href="https://apiad.github.io/opencode">opencode toolkit</a> repository. It&#8217;s still pretty much work in progress, so use it with care.</p><p>In future articles I will explore specific problems in more detail and discuss concrete strategies to implement powerful workflows that keep you, the user, in absolute control, while delegating the majority of the grunt work.</p><p>And, as a final remark, I&#8217;m seriously considering building my own CLI agent. I know, I know. Reinventing the wheel and all that. But my plan is not to compete with any of the professional tools out there. What I always care about is <em>understanding</em> things deeply, and as my computer science career has taught me so far, there is no deeper understanding than the one you gain from actually building stuff.</p><p>So stay tuned for that. I will share progress as usual in the form of educational articles, so you&#8217;ll get to see under the hood how to build a fully functional CLI agent with tool calling, context compaction, skills, commands (the powerful ones, not the cheap single-prompt injection), subagent delegation, sandboxing, and all the engineering design hurdles that come with it.</p><p>Until next time, stay curious.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Fun quirk. Typing <code>/etc/host</code> plus the <code>s</code> makes Substack silently fail on draft save, some sort of ill-defined security rule, I suppose. What the f&#8230;</p></div></div>]]></content:encoded></item><item><title><![CDATA[How I'm Using AI Today]]></title><description><![CDATA[Yes, another take on this. I promise you this one is a bit different.]]></description><link>https://blog.apiad.net/p/how-im-using-ai-today</link><guid isPermaLink="false">https://blog.apiad.net/p/how-im-using-ai-today</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Mon, 02 Mar 2026 21:13:04 GMT</pubDate><enclosure url="https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="5663" height="4599" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:4599,&quot;width&quot;:5663,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;clothes iron, hammer, axe, flashlight and pitcher on brown wooden table&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&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="clothes iron, hammer, axe, flashlight and pitcher on brown wooden table" title="clothes iron, hammer, axe, flashlight and pitcher on brown wooden table" srcset="https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHx0b29sc3xlbnwwfHx8fDE3NzI0NTU2Mzl8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 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><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@toddquackenbush">Todd Quackenbush</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>I know, this is the probably the twentieth article you&#8217;ve read this week on &#8220;how I am using AI to automate my whole life/work/whatever&#8221;. Me too. But please, give me a couple of minutes to tell why this article might be different, and thus interesting to you.</p><p>But even if you don&#8217;t have two minutes to spare, please check the <a href="https://github.com/apiad/starter">repository</a> where all I&#8217;m going to tell you about is implemented, ready for your taking. Clone it and play with it, then if you like it, come back and read the rationale behind it.</p><p>Done? Ok, here we go.</p><p>So yes, this is another article trying to explain to you how I use AI coding agents (specifically Gemini CLI, but the specifics don&#8217;t matter) to enhance my workflows. Here are a couple of reasons why I think you might be interested&#8212;and why this article might be different to so many lookalikes out there.</p><p>First, I&#8217;m not an enthusiastic techbro who just discovered AI. If you&#8217;ve read this blog before, you know I&#8217;m a longtime researcher in AI&#8212;way before LLMs were a thing&#8212;and also a self-proclaimed AI anti-hypist. I&#8217;m not just overexcited about this shiny new toy. I&#8217;ve been using generative AI since day one for everything, and I&#8217;ve been telling you exactly how it sucks at almost anything important since day one. Coding has been the same until very recently. I can tell you the change in productivity is real, provided you are responsible and considerate.</p><p>Second, my approach to incorporating AI into my workflows is very careful and grounded in a large dose of healthy skepticism. I know firsthand how these things fail, so my approach attempts to be very robust to hallucinations and context drift and all the plagues of even the most powerful LLMs.</p><p>Third, I have a thing for systems. What I&#8217;m going to show is not just a set of hacks or clever prompts or productivity tips. It&#8217;s a principled system to go from ideation to research and planning to execution at the fastest responsible speed, without sacrificing on safety or maintainability.</p><p>And fourth, I have kind of a unique position in that I&#8217;m both heavily invested into coding as well as technical writing. I&#8217;m a college professor, so I do a lot of research, writing, and editing; but I also run a small AI startup, small enough that I get to do a large part of the coding. So my system attempts to bridge these two facets&#8212;code and prose creation&#8212;with the same unified principles of careful deliberation and planning, and robust tracking of the project evolution.</p><p>If you&#8217;re curious already, let me start by exposing the overall principles behind this approach, and then we&#8217;ll dive (not delve, but close) into the details.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>Principles of Effective AI-Assisted Work</h2><p>By far the most pressing limitation of modern, top-tier LLMs for my line of work is context saturation. What I mean by this, is no matter how many tokens your model can fit (Gemini 3 claims to chug as much as 1 million tokens), when you work for a considerable amount of time on a single project, you will exhaust this context. And even if the context window isn&#8217;t full, the model will quickly lose the capability to keep track of the important bits of context, and will start to deviate from your instructions and make up its own agenda. Not out of evilness but simple probabilities.</p><p>The way you see this problem when using Claude Code, Gemini CLI, Codex, Copilot, or anything similar is twofold. The model will either forget midtask what is was supposed to do, but it won&#8217;t simply fail; it will reinterpret the task based on its faulty, lossy, blurry viewpoint given the available context and it will do something close, but not quite what you wanted. Or, the model will make faulty assumptions and forget to ask relevant questions, again behaving close but not quite exactly as you intended.</p><p>The result is always the same, you get frustrated that you achieved 95% of what you needed, but the remaining 5% is harder to fix than to just try again. And what could have been a happy working session where you get important and difficult things done quickly derails into a session of arguing with an LLM trying to convince it (him/her/pick your side) to do things the way you want.</p><p>Barring any fundamental paradigm shifts in the near future, under the assumption that LLMs will keep working as they are, the only way to fix this is to be very conscious and careful about the context&#8212;what has been dubbed context engineering&#8212;in two senses: First, do not pollute the context with unnecessary details. And second, re-inject into the context whatever is relevant for any given task, so it doesn&#8217;t get forgotten.</p><p>I know, kind of contradictory, but tradeoffs are what engineering is all about. In my system, I&#8217;ve applied three principles to help me manage these tradeoffs effectively.</p><ol><li><p>The important things should be made explicit.</p></li><li><p>Resist the urge to guess.</p></li><li><p>Delegate, delegate, delegate (yeah, three times).</p></li></ol><p>Here&#8217;s how that works. For principle one, we will keep track of everything important in markdown files in the repository. This means ideas are committed to plans in markdown files before acting on them, research is summarized and stored in real time, and everything that changes in the project is logged to a long-lived journal, so the model remembers why we made some decision months ago.</p><p>For principle two, we will favor using explicit commands that are translated into explicit prompts, instead of relying on implicitly activated skills that you have little control of. So if you want the model to make a plan, you will prompt it with &#8220;/plan lets design feature X&#8221;, and the <code>/plan</code> command will invoke a carefully crafted prompt that says how plans work, where they are stored, etc.</p><p>And principle three means using sub-agents a lot. This is a Gemini CLI specific feature&#8212;but every other coding agent has a similar thing&#8212;where you can launch a complicated task as a &#8220;sub-agent&#8221;&#8212;which basically means a custom prompt&#8212;but here is the important part: All the context of that sub-agent is kept private, not shared with the main agent, so the internal reasoning the sub-agent needed to run to find 20 different sources in Google does not pollute the main context. We only receive back the summarized responses. This allows running very long tasks (my record is a 30 minutes long research loop, involving hundreds of retrieved web pages) on a single agent turn, without exhausting the context.</p><p>I use four sub-agents in different commands. The <code>planner</code> is the lead architect. It&#8217;s a read-only agent that walks through your codebase and reads everything necessary to understand architecture, design decisions, etc., given a specific task. It then produces a detailed Markdown plan in the <code>plans/</code> directory&#8212;a physical source of truth that you can review before any code is touched, and that the main agent will follow step-by-step. This separation prevents the system from &#8220;guessing&#8221; its way through your codebase.</p><p>When I need external knowledge&#8212;like a library&#8217;s latest API or a specific technical specification&#8212;the <code>researcher</code> agent takes over. It scours the web to fetch relevant documentation, which it then synthesizes into granular summaries in the <code>research/</code> directory. This raw data is then handed off to the main agent to build an executive report annotated and linked to all relevant sources, again all stored already in your repository.</p><p>And there are two more agents, specifically designed for technical writing. The <code>reporter</code> agent takes an outline, and a folder of content, and it will write section by section, a detailed account of what the outline requested.</p><p>Unlike a standard LLM that might provide a high-level summary, the <code>reporter</code> is trained to expand specific placeholders with deep, evidence-based paragraphs. It draws directly from your <code>research/</code> files and the project <code>journal/</code> to ensure every sentence is grounded in the project&#8217;s actual state. Finally, the <code>editor</code> provides the final polish, auditing the draft for structural gaps and linguistic tics. It is grounded in a customizable style guide to make sure it always respects your style.</p><p>This distributed intelligence is held together by a central nervous system of context files, as per principle one. A <code>journal/</code> directory provides a chronological record of decisions and progress, acting as a long-term memory for the project. The <code>plans/</code> directory stores the strategic intent, while a <code>TASKS.md</code> file provides a high-level overview of the project&#8217;s current status. This structured environment allows the subagents to maintain a high degree of situational awareness without needing to ingest the entire repository in every turn.</p><p>Now that we have the key pieces in place, you can start to see why I think this approach is powerful. It is very extensible&#8212;you can add new agents triggered by specific commands to customize any kind of workflow&#8212;and it mostly solves the main pain point of modern LLMs, which is precisely the brittleness of long contexts.</p><h2>Workflow Details</h2><p>With all that, let me show you the specific commands and workflows I have currently implemented, but keep in mind what follows is but one example of the kind of powerful workflows we can start to automate.</p><p>I will divide the rest of the article into a few major areas, and explain the commands and agents that I use in each case, and a bit of the high-level instructions given to each of them.</p><h3>Discovery &amp; Strategy</h3><p>The most critical phase of any project occurs before I write a single line of code. I call this the &#8220;Discovery and Strategy&#8221; phase, powered by the <code>/research</code> and <code>/plan</code> commands. By formalizing this process, I&#8217;ve moved away from impulsive execution toward a deliberate, architected approach.</p><p>The <code>/research</code> command is my primary tool for external knowledge. When triggered, the <code>researcher</code> scours the web for technical documentation and relevant case studies, synthesizing them into granular summaries in the <code>research/</code> directory. These files become a persistent knowledge base, allowing me to reference verified facts without leaving my terminal.</p><p>Complementing this is the <code>/plan</code> command, which focuses on internal strategy. The <code>planner</code> conducts a thorough analysis of the codebase and the <code>journal/</code> to understand the system&#8217;s current state. After an interactive dialogue to resolve any ambiguity, it produces a comprehensive Markdown plan in the <code>plans/</code> directory. This document maps out the technical territory and provides a step-by-step execution roadmap.</p><p>The strength of this workflow lies in the synergy between these two tools. A <code>/plan</code> operation might reveal a gap in my understanding of a specific library, prompting a targeted <code>/research</code> session. Conversely, a new research finding might shift my technical direction, leading to a refined plan.</p><h3>Software Development</h3><p>Once I have a solid strategy in my <code>plans/</code> directory, I can move into execution. Let&#8217;s focus now on software development. I&#8217;ve designed four core commands&#8212;<code>/issues</code>, <code>/task</code>, <code>/commit</code>, and <code>/release</code>&#8212;to eliminate the friction of context-switching between my IDE and my terminal.</p><p>The cycle starts with <code>/issues</code> and <code>/task</code>. The <code>/issues</code> command acts as an expert project lead, interfacing directly with the GitHub CLI to analyze open issues and recommend what to tackle next based on strategic impact. For roadmap tracking, the <code>/task</code> command manages a living <code>TASKS.md</code> document. It assesses the value of pending work to ensure my efforts are always aligned with the project&#8217;s goals.</p><p>As I translate the plan into code, the <code>/commit</code> command brings order to my workspace. Instead of a monolithic &#8220;wip&#8221; commit that hides the logic of my changes, the system analyzes the <code>git diff</code> and logically groups modifications into cohesive units. It separates a core feature update from a documentation tweak, then proposes a series of atomic, Conventional Commits for my approval. This keeps my version history pristine and easy to navigate.</p><p>The final stage is deployment. Manual releases are fragile processes fraught with repetitive checklists: bumping versions and running tests before managing tags. The <code>/release</code> command automates this entire sequence. It verifies the workspace integrity by ensuring a clean git tree and passing tests via <code>make</code>. It then analyzes the commit history to propose the next version bump, drafts a <code>CHANGELOG.md</code> entry, and publishes the final tag to GitHub. This transforms a tedious afternoon of housekeeping into a single-command operation.</p><p>But, as you&#8217;ve seen, everything happens in tandem with those principles. No important action is taken without my confirmation, and everything gets logged into the filesystem, so all future decisions are grounded in past experience.</p><h3>Content Creation</h3><p>Now let&#8217;s focus on writing high-quality documentation and long-form articles. This is perhaps the most sensible part of the article (and the system) because people are <em>very</em> sensitive today with the topic of AI writing&#8212;and rightly so. Again, my intention here is to enhance how I work and get stuff done. If you&#8217;re writing for the pleasure of doing it, that&#8217;s totally fine, you probably don&#8217;t want any help there.</p><p>Anyway, the approach is built on the same cognitive foundation as the development path: the research and plans gathered during the discovery phase should serve as grounding for writing.</p><p>It starts with the <code>/draft</code> command. In its initial phase, the system performs a deep scan of the <code>research/</code> and <code>plans/</code> directories to identify the key themes relevant to the requested topic. If the foundation is too thin, the system will pause and suggest a <code>/research</code> or <code>/plan</code> cycle to ensure the draft has sufficient substance. Once the context is validated, the workflow enters an interactive &#8220;Outline Creation&#8221; phase. Rather than guessing at a structure, the system proposes a detailed Markdown outline. This collaborative step allows me to set the narrative arc and logical flow that I want, iterating on the high-level structure of, say, a technical article, before committing on the details.</p><p>Once the outline is locked, the <code>/draft</code> process initializes a skeleton file&#8212;complete with section headers and strategic placeholders&#8212;and then moves into an iterative, section-by-section expansion. Here, the <code>reporter</code> subagent takes the lead. Guided by the specific context of each section, the <code>reporter</code> weaves together research summaries and technical specifications into professional prose, all grounded on a style guide document.</p><p>Because the expansion happens in granular steps, the system maintains a high level of detail that a single-shot generation would inevitably lose. The result is a first draft that is structurally sound and rich with technical depth.</p><p>However, a first draft is rarely the final word. It will always sound AI-ish, and for many other reasons, it is rarely good enough. To achieve professional quality, I use the <code>/revise</code> command, which runs a structural and linguistic audit powered by the <code>editor</code> subagent following the same style guide.</p><p>Unlike a simple &#8220;check my writing&#8221; prompt, the <code>editor</code> performs a deep analysis of the document&#8217;s flow and tone. It identifies logical gaps where more evidence might be needed and highlights awkward phrasing that could obscure my intent. And crucially, this isn&#8217;t an automated &#8220;fix-all&#8221; tool; it&#8217;s an interactive process. The system presents its findings and proposes specific improvements, which I can then review or approve.</p><p>This collaborative refinement process ensures the final output maintains a consistent, professional voice while benefiting from the speed of the AI. By using <code>/revise</code>, I can surgically improve the text to enhance clarity and impact without losing control over the narrative.</p><p>But, in any case, I always find necessary a manual review and editing after all the AI enhancements. It shouldn&#8217;t be a surprise to you that this article is written in this way, but what you&#8217;re reading now is probably 80% different to what the final <code>/revise</code> iteration gave me. There is only so much you can prompt an AI, and that final human touch is not part of it.</p><p>But that&#8217;s good. This automates the first 80% or so of compiling a gazillion sources into a coherent narrative, and leaves the remaining 80% of polishing for me, which is the part I actually enjoy about writing.</p><h3>Background Tasks</h3><p>But there&#8217;s more. All of the above is what happens during, let&#8217;s say, the work day. That&#8217;s me sitting in front of the terminal, typing commands, approving stuff, fixing and redirecting, etc. Being an orchestrator.</p><p>But the real magic of AI-assisted development is what happens when you&#8217;re not looking. How you can leave you AI assistant working through the night, compiling sources, fixing bugs and proposing pull requests, enhancing the test suite, burning tokens your behalf.</p><p>To achieve this, I built an automation layer via the <code>/cron</code> command. The heart of this automation is the <code>cron.toml</code> file. This configuration file allows me to define scheduled tasks with a simple, declarative syntax. Each task specifies a name, an execution schedule, and a natural language prompt for the AI to execute.</p><p>For instance, I can schedule a task to perform &#8220;Background Research&#8221; every midnight on the unfinished tasks, scouring the web for new developments in a specific technical niche or finding specific sources to deal with the recently discovered bugs. By offloading these repetitive tasks, I ensure the knowledge base remains fresh and the project&#8217;s momentum never stalls. When the morning arrives, we have a lot of new context to start planning the day&#8217;s bugfixes and feature developments.</p><h2>Maintenance &amp; Refactoring</h2><p>Now, for the final touch, here&#8217;s how I deal with technical debt and feature rot. As a project evolves&#8212;and especially, as fast as AI-powered projects evolve&#8212;it accumulates technical debt&#8212;outdated implementations, untested paths, and plain old useless features&#8212;but also, contextual debt&#8211;&#8212;outdated plans and completed tasks that clutter the roadmap, and research we never acted upon.</p><p>Without deliberate intervention, this noise degrades the AI&#8217;s performance, leading to context rot. The <code>/maintenance</code> command is my primary defense against this entropy. It treats the development environment as a living instrument that I must regularly tune and sharpen to maintain its efficiency.</p><p>The <code>/maintenance</code> workflow follows the same plan-first architecture as the rest of the system. When invoked, the AI performs a comprehensive audit of the codebase, focusing on improvements like code readability and performance optimization. It identifies opportunities to apply the DRY (Don&#8217;t Repeat Yourself) principle and ensures that every function is documented with high-quality docstrings. But it also fixes deviations between the documentation and the actual implementation.</p><p>Crucially, this is an interactive process: the system presents a detailed refactoring plan for my approval before making any changes. This ensures that I remain in control while the machine handles the labor of cleaning the code.</p><p>Beyond code refactoring, I maintain system health through disciplined repository hygiene. A key component is the management of the <code>TASKS.md</code> file. By regularly moving completed items into the &#8220;Archive&#8221; section, I ensure that my primary operational view remains focused on what is relevant. This simple act of archiving prevents the &#8220;Active Tasks&#8221; list from becoming a source of distraction.</p><p>The goal of these maintenance practices is to provide the AI with the cleanest possible line of sight into the project&#8217;s state. When the repository is cluttered with stale research, the subagents are forced to sift through irrelevant data, increasing the risk of hallucinations. By treating maintenance as a first-class citizen, I ensure that every interaction, whether a <code>/plan</code> or a <code>/draft</code>, is grounded in a precise context.</p><h2>Conclusion</h2><p>This system is far from done, and as models improve in capabilities I&#8217;m sure we&#8217;ll unlock new areas for automation and augmentation that we cannot think about today. But for me, the key principles will remain valid for a long time. These are principles of robust engineering and management, after all. You can read them thinking of a completely human-based organization, and it&#8217;s all valid:</p><ol><li><p>The important things should be made explicit.</p></li><li><p>Resist the urge to guess.</p></li><li><p>Delegate, delegate, delegate (yeah, three times).</p></li></ol><p>And this is the key insight for me. Good AI users are basically good managers. All the science and engineering behind good practices for people management also apply to good AI management. And then there are of course technical considerations because AIs are not people, and perhaps never will.</p><p>So this is perhaps the most philosophical take-away from this article. Sorry to have made you read so long for this!</p><p>Now, on the technical side, please do check the <a href="https://github.com/apiad/starter">repository</a> and play with it. There are a couple extra goodies I haven&#8217;t tell you about, like a <code>/scaffold</code> command that creates new projects from scratch, and an <code>/onboard</code> command that explains the whole repository in detail. Those are particularly useful if you&#8217;re reusing this repository as a template to start your own project.</p><p>Ultimately, this repository is not a one-size-fits-all solution. It is a starting point. The commands and subagents provided here represent a particular opinion on how modern development should look, but they are not the only way. The power of this framework lies in its extensibility. Every system prompt for agents and commands is a living document, meant to be tweaked and rewritten to suit your unique mental model.</p><p>So if you do try it out, please let me know in the comments. And if you have a different (or similar) system set up for yourself, please share with all us your experience and your thoughts. We are all learners in this era of AI, and we can only help each other.</p><p>Stay curious.</p>]]></content:encoded></item><item><title><![CDATA[Drawing (not so) Beautiful Diagrams with Pure Python]]></title><description><![CDATA[Ups, I did it again :)]]></description><link>https://blog.apiad.net/p/drawing-not-so-beautiful-diagrams</link><guid isPermaLink="false">https://blog.apiad.net/p/drawing-not-so-beautiful-diagrams</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Fri, 13 Feb 2026 14:06:17 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/21624634-7c05-40c2-98f0-46ed20132c3d_663x359.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Over the past couple of weeks, I&#8217;ve been working on a pure Python library for rendering SVGs. In a spur of unbridled inspiration, I called it <strong><a href="https://apiad.github.io/tesserax">Tesserax</a></strong>. Long story short, it got <em>waaayyyy</em> beyond control, as it happens. </p><p>This article is a quick recap of why I did it, how I went down the rabbit hole of implementing yet-another-drawing-library, and a small showcase of what it can do it can do, in case you want to give it a shot.</p><p>But before moving one, here is a minimal example of what you can do with a few lines of Python.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!R3IB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!R3IB!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif 424w, https://substackcdn.com/image/fetch/$s_!R3IB!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif 848w, https://substackcdn.com/image/fetch/$s_!R3IB!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif 1272w, https://substackcdn.com/image/fetch/$s_!R3IB!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!R3IB!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif" width="320" height="78" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:78,&quot;width&quot;:320,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!R3IB!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif 424w, https://substackcdn.com/image/fetch/$s_!R3IB!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif 848w, https://substackcdn.com/image/fetch/$s_!R3IB!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif 1272w, https://substackcdn.com/image/fetch/$s_!R3IB!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93a97f55-54b7-49a8-9c5f-f2f21a862d7e_320x78.gif 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Are you curious now? Then read on!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.apiad.net/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://blog.apiad.net/subscribe?"><span>Subscribe now</span></a></p><h2>The Case for Tesserax</h2><p>There are a gazillion tools out there for drawing mathematical diagrams in Python, from the ancient, trustable <strong>matplotlib</strong> to the new cool kid in the park <strong>manim</strong>. There are a ton of charting libraries (those intended to draw bars, pies, and doughnuts) like <strong>altair</strong> and <strong>plotly</strong>; and a ton of high-level diagram makers like <strong>graphviz</strong> for graphs and networks, and <strong>mermaid</strong> for, well, whatever the mermaid devs think is worth drawing.</p><p>The problem with all these is that, one, <em>there weren&#8217;t made by me</em>. Now, jokes apart, I do believe there is a lot of value in reinventing the wheel, if only because you get to learn a lot about wheel&#8212;and boy have I learned more than I ever wanted to about SVG in the last couple of weeks. But also, you may find that your wheel fits your cart slightly better than all others, just because you know precisely what you need (and enough with wheel analogies).</p><p>So, I made Tesserax to cover a sweet spot that I couldn&#8217;t find anywhere: a lightweight library (literally zero dependencies, not even <strong>numpy</strong>) that renders web-native content (so everything scales and layouts perfectly in Jupyter / Quarto) and has both a very powerful low-level engine for when you want pixel-perfect control, and a very comfortable high-level engine for the most typical workflows.</p><p>Also, it should support <em>animations</em>. But I&#8217;m getting ahead of myself.</p><h2>What Can Tesserax Do</h2><blockquote><p>I won&#8217;t put any code in this article because, one, Substack sucks at code. And two, it would only make the article harder to follow. All of these examples are fully described in the <a href="https://apiad.github.io/tesserax">online documentation</a>.</p></blockquote><p>At its core, <strong>Tesserax</strong> is a library for defining an SVG scene. You create a <strong>Canvas</strong>, and some <strong>Shapes</strong> to it, define some attributes, and render it as an SVG file. Simple enough<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!67lT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!67lT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png 424w, https://substackcdn.com/image/fetch/$s_!67lT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png 848w, https://substackcdn.com/image/fetch/$s_!67lT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png 1272w, https://substackcdn.com/image/fetch/$s_!67lT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!67lT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png" width="399" height="128" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:128,&quot;width&quot;:399,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4287,&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://blog.apiad.net/i/187845763?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.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_!67lT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png 424w, https://substackcdn.com/image/fetch/$s_!67lT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png 848w, https://substackcdn.com/image/fetch/$s_!67lT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png 1272w, https://substackcdn.com/image/fetch/$s_!67lT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c88048f-2c2c-4f68-a567-b01f783e12f4_399x128.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Beyond basic primitives, you can of course draw arbitrary paths and style them easily. Here is a <strong>Polyline</strong> primitive that allows controlling the curvature with a <strong>smoothness</strong> factor.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YaVP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YaVP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.png 424w, https://substackcdn.com/image/fetch/$s_!YaVP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.png 848w, https://substackcdn.com/image/fetch/$s_!YaVP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.png 1272w, https://substackcdn.com/image/fetch/$s_!YaVP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YaVP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.png" width="282" height="270.3248407643312" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:301,&quot;width&quot;:314,&quot;resizeWidth&quot;:282,&quot;bytes&quot;:5697,&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://blog.apiad.net/i/187845763?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.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_!YaVP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.png 424w, https://substackcdn.com/image/fetch/$s_!YaVP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.png 848w, https://substackcdn.com/image/fetch/$s_!YaVP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.png 1272w, https://substackcdn.com/image/fetch/$s_!YaVP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8960d96f-f48b-4c2c-af1c-0f4bcfb8b579_314x301.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>You can control and distort these shapes in any form you want, including fully procedural warping of the edges.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ifdW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ifdW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.png 424w, https://substackcdn.com/image/fetch/$s_!ifdW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.png 848w, https://substackcdn.com/image/fetch/$s_!ifdW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.png 1272w, https://substackcdn.com/image/fetch/$s_!ifdW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ifdW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.png" width="412" height="152.10970464135022" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1226efa9-359b-4b1a-a986-19eed93df165_474x175.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:175,&quot;width&quot;:474,&quot;resizeWidth&quot;:412,&quot;bytes&quot;:9834,&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://blog.apiad.net/i/187845763?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.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_!ifdW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.png 424w, https://substackcdn.com/image/fetch/$s_!ifdW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.png 848w, https://substackcdn.com/image/fetch/$s_!ifdW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.png 1272w, https://substackcdn.com/image/fetch/$s_!ifdW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1226efa9-359b-4b1a-a986-19eed93df165_474x175.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>And, of course, since no mathematical drawing library is worth a penny without a sketchy mode, you can also do that (albeit with some constraints).</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3DwH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3DwH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.png 424w, https://substackcdn.com/image/fetch/$s_!3DwH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.png 848w, https://substackcdn.com/image/fetch/$s_!3DwH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.png 1272w, https://substackcdn.com/image/fetch/$s_!3DwH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3DwH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.png" width="403" height="171.6632860040568" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d62b252a-8212-4144-a857-d7e8d9944767_493x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:493,&quot;resizeWidth&quot;:403,&quot;bytes&quot;:11596,&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://blog.apiad.net/i/187845763?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.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_!3DwH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.png 424w, https://substackcdn.com/image/fetch/$s_!3DwH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.png 848w, https://substackcdn.com/image/fetch/$s_!3DwH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.png 1272w, https://substackcdn.com/image/fetch/$s_!3DwH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd62b252a-8212-4144-a857-d7e8d9944767_493x210.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>To make positioning and layout easy, Tesserax comes a set of builtin layouts (that of course can be extended in any way you want, it&#8217;s Python) for common patterns like rows, columns, grids&#8230;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tEXd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tEXd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png 424w, https://substackcdn.com/image/fetch/$s_!tEXd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png 848w, https://substackcdn.com/image/fetch/$s_!tEXd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png 1272w, https://substackcdn.com/image/fetch/$s_!tEXd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tEXd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png" width="349" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:349,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:858,&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://blog.apiad.net/i/187845763?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.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_!tEXd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png 424w, https://substackcdn.com/image/fetch/$s_!tEXd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png 848w, https://substackcdn.com/image/fetch/$s_!tEXd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png 1272w, https://substackcdn.com/image/fetch/$s_!tEXd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60ca74b0-8ca0-4522-93b2-046664db44e3_349x210.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>And a couple of complex layouts for things like trees and arbitrary graphs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KodJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KodJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.png 424w, https://substackcdn.com/image/fetch/$s_!KodJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.png 848w, https://substackcdn.com/image/fetch/$s_!KodJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.png 1272w, https://substackcdn.com/image/fetch/$s_!KodJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KodJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.png" width="192" height="333" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:333,&quot;width&quot;:192,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7101,&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://blog.apiad.net/i/187845763?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.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_!KodJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.png 424w, https://substackcdn.com/image/fetch/$s_!KodJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.png 848w, https://substackcdn.com/image/fetch/$s_!KodJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.png 1272w, https://substackcdn.com/image/fetch/$s_!KodJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dc8f82b-67f7-424f-9f0b-abe7e1fd0085_192x333.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>But things start to get really interesting when you discover that if you can render an image from code, then you can render as many as you want! And that leads to&#8230; animations!</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5ssP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5ssP!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif 424w, https://substackcdn.com/image/fetch/$s_!5ssP!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif 848w, https://substackcdn.com/image/fetch/$s_!5ssP!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif 1272w, https://substackcdn.com/image/fetch/$s_!5ssP!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5ssP!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif" width="172" height="107.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:100,&quot;width&quot;:160,&quot;resizeWidth&quot;:172,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!5ssP!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif 424w, https://substackcdn.com/image/fetch/$s_!5ssP!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif 848w, https://substackcdn.com/image/fetch/$s_!5ssP!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif 1272w, https://substackcdn.com/image/fetch/$s_!5ssP!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23b3b8e-d4af-4a9b-ac8b-b09474037e25_160x100.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Tesserax animations are procedural, meaning you write code that defines how each object changes in time. But there is a very high-level API for defining and composing animations declaratively, which means you seldom need the full power of tweaking each shape properties (but that power is there when you need it, as it should).</p><p>And, of course, once you grok animations, the next immediate idea is <em>physically-based animations</em>. (This is where things got way out of control, as you may imagine). Tesserax comes with a very small but fully-fledged pure Python 2D physics engines&#8212;that was a mouthful. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xao8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xao8!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif 424w, https://substackcdn.com/image/fetch/$s_!xao8!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif 848w, https://substackcdn.com/image/fetch/$s_!xao8!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif 1272w, https://substackcdn.com/image/fetch/$s_!xao8!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xao8!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif" width="126" height="262.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:250,&quot;width&quot;:120,&quot;resizeWidth&quot;:126,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!xao8!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif 424w, https://substackcdn.com/image/fetch/$s_!xao8!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif 848w, https://substackcdn.com/image/fetch/$s_!xao8!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif 1272w, https://substackcdn.com/image/fetch/$s_!xao8!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07c8667d-af5a-47ec-8471-6941baeb543b_120x250.gif 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>It can resolve collisions among arbitrary objects (using circles and rotated boxes as approximate colliders) and simulate fixed-step rigid body mechanics. Physic animations are baked&#8212;which means you compute the animation once and then render it. So, no, no 2D games for you! What were you thinking about? This is a graphing library, for Turing&#8217;s sake!</p><p>The purpose of this physics engine is, once again, didactic&#8212;e.g., to explain physics concepts in a Jupyter notebook with a bit of shizzass. </p><h2>Final Words</h2><p>I started writing Tesserax to help me create diagrams and animations for my lectures and articles. But when you do that with <em>a fully-fledged programming language</em> things start to get really interesting, because now you can leverage your well-honed programming skills to create complex diagrams with the minimum necessary code. You can encapsulate repeatable patterns into classes and methods, and build impressive diagrams bottom-up. You can abstract common patterns into drawable concepts like trees, automatons, etc.</p><p>For example, in the docs you will find a bit of code to implement a blob-like simulation by creating a<strong> ConvexHull</strong> component that automatically tracks inner shapes and builds the convex surrounding path around them. Tied with some simple physics (a bunch of balls interconnected with springs) gives you this ugly-looking blob of dread that I hope haunts your dreams for the next week or so&#8212;it sure will mine.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FD07!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FD07!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif 424w, https://substackcdn.com/image/fetch/$s_!FD07!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif 848w, https://substackcdn.com/image/fetch/$s_!FD07!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif 1272w, https://substackcdn.com/image/fetch/$s_!FD07!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FD07!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif" width="436" height="272.8516129032258" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:388,&quot;width&quot;:620,&quot;resizeWidth&quot;:436,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!FD07!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif 424w, https://substackcdn.com/image/fetch/$s_!FD07!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif 848w, https://substackcdn.com/image/fetch/$s_!FD07!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif 1272w, https://substackcdn.com/image/fetch/$s_!FD07!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7820fc03-0d3d-43bb-8c37-0126e27b5652_620x388.gif 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>On a more serious note, the next step for Tesserax is building a library of reusable math and computer science concepts, like animated arrays, trees, graphs, etc., that can be used to explain complex CS topics with the much need visual assistance. As the great Donald Knuth once said, an algorithm must be seen to be believed. Tesserax is here to help you see them.</p><p>I&#8217;d really love if you guys would give Tesserax a try, now that is a bit more polished, and shoot me all your questions and suggestions. Special thanks to <span class="mention-wrap" data-attrs="{&quot;name&quot;:&quot;Wyrd Smythe&quot;,&quot;id&quot;:195807185,&quot;type&quot;:&quot;user&quot;,&quot;url&quot;:null,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f88cc94-56a6-4453-b3dd-c8ccb9194c15_2316x2316.png&quot;,&quot;uuid&quot;:&quot;45140a44-685f-4df2-95a6-819f2f652900&quot;}" data-component-name="MentionToDOM"></span> for many insightful comments and suggestions (although I&#8217;m not sure I&#8217;ve totally addressed all of them :)</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Sadly, among all the stupid things that Substack cannot do for the sake of being annoying, it seems rendering SVG is one of them. So all drawings in this article are dumb screenshots of what otherwise would be pixel-perfect, infinitely scalable SVGs.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Introducing Tesserax]]></title><description><![CDATA[A pure Python, developer-friendly library for academic drawing.]]></description><link>https://blog.apiad.net/p/introducing-tesserax</link><guid isPermaLink="false">https://blog.apiad.net/p/introducing-tesserax</guid><dc:creator><![CDATA[Alejandro Piad Morffis]]></dc:creator><pubDate>Sat, 31 Jan 2026 17:24:56 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="6491" height="4447" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:4447,&quot;width&quot;:6491,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;sketch pad and coloring pens&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&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="sketch pad and coloring pens" title="sketch pad and coloring pens" srcset="https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1525278070609-779c7adb7b71?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw4fHxkcmF3aW5nfGVufDB8fHx8MTc2OTg3OTkwOHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 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><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@kobuagency">KOBU Agency</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>It is 2026. We are still treating scientific diagrams like cave paintings.</p><p>I have a PhD in Machine Learning. I can explain the mathematics of high-dimensional manifold optimization. Yet, until recently, if I wanted to draw two boxes connected by an arrow, I had to Google the syntax.</p><p>My diagrams were dead artifacts. While my code was version-controlled and modular, my figures were fragile binary blobs or brittle LaTeX macros. I needed my drawings to behave like the rest of my research.</p><p>Instead, I found myself trapped between two equally bad options.</p><p>I have spent weekends fighting <strong>Mermaid</strong> and <strong>Graphviz</strong>. They are fine for a quick flowchart. But try asking them for a publication-quality Turing machine. Try asking for a precise computer architecture diagram. You hit a wall.</p><p>Using these black-box layout engines is like trying to parallel park a bus while shouting instructions from the sidewalk. You can nudge the parameters, but you never quite end up where you want to be.</p><p>The real sin of these tools is their refusal to encapsulate. As developers, we take parameterization for granted. We define logic once and instantiate it a thousand times. But you cannot take a Mermaid diagram, wrap it in a function, and ask it to render a version with &#8220;n&#8221; nodes. Our drawing tools should respect the engineering principles we teach.</p><p>Then there is <strong>TikZ</strong>.</p><p>TikZ is the undisputed masterpiece of academic drawing. It offers total control. It is also fundamentally broken for the web.</p><p>TikZ is a &#8220;print-first&#8221; citizen. If you convert it to SVG for a blog post, you don&#8217;t get a clean vector drawing. You get a crime scene. Inspect the element, and you will see a soup of unreadable paths that no CSS can touch.</p><p>And then, there is the &#8220;Reviewer #2&#8221; problem. <em>(Damn Reviewer #2, why is it always you?!)</em> You spend three hours perfecting a TikZ architecture diagram. You submit the paper. The review comes back: <em>&#8220;Please swap layer 3 and layer 4.&#8221;</em> In a drag-and-drop tool, this is annoying. In TikZ, it is a reconstruction project involving fifty lines of coordinate hard-coding. It isn&#8217;t just tedious; it&#8217;s fragile.</p><p>To render my documentation in a CI/CD pipeline, I shouldn&#8217;t have to install four gigabytes of TeX Live just to draw a few circles. And there is the friction of the language itself. Writing geometry macros in a specialized DSL is a jarring context switch. It forces you to leave the logic of your Python research and enter a world of backslashes and curly braces just to visualize a result.</p><h2>Meet Tesserax</h2><p>The shift happened when I moved my academic publishing to <strong>Quarto</strong>.</p><p>Quarto is essentially Jupyter on steroids. Since Jupyter is the standard for scientific computing, the solution was obvious. We didn&#8217;t need another GUI. We didn&#8217;t need a new domain-specific language. We needed a Python library that could live inside the notebook and speak fluent SVG.</p><p>I built <strong><a href="https://github.com/apiad/tesserax">Tesserax</a></strong> to close the gap. I started with the atoms, like Rects, Circles, and Paths. But the breakthrough was the Anchor System. I wanted a &#8220;nervous system&#8221; for diagrams, where objects know where they are relative to one another. No manual coordinates. No magic numbers.</p><p>Tesserax is what happens when you treat a drawing as a function of state.</p><p>In Tesserax, you don&#8217;t place a node at <code>(100, 200)</code>. You define a <code>Layout</code>. You say, &#8220;I want a column of nodes, and for every node, I want an arrow pointing to the next one.&#8221;</p><pre><code><code># This isn't just a drawing. It's logic.
with Canvas() as c:
    nodes = [Circle(10) for _ in range(5)]
    layout = Column(nodes, gap=20)

    # If I add a node to the list above, the arrows update automatically.
    for n1, n2 in pairwise(nodes):
        Arrow(n1.anchor("bottom"), n2.anchor("top"))</code></code></pre><p>This snippet doesn&#8217;t just render a figure. It renders <em>any</em> version of that figure. It captures the abstraction, not just the pixels.</p><h2>The Rationale Behind Tesserax</h2><p><em>But why, Alex, why building something like this? Don&#8217;t you have enough in your plate now?</em></p><p>Well, first, who are you and why do you know me so well? Second, it&#8217;s fun! But if that isn&#8217;t enough by itself, let&#8217;s analyze the landscape of modern academic drawing.</p><p>First, there is <strong>Matplotlib</strong>. </p><p>It is the industry standard for a reason. If I have a CSV of training data, Matplotlib is my first call. But have you ever tried to draw a linked list in it? You end up fighting the axes. You spend a dozen lines of code just hiding the ticks and borders to get a blank canvas.</p><p>Matplotlib is a library built for statistics that is occasionally forced to perform geometry. It feels like doing calligraphy with a highlighter. Tesserax respects the division of labor: use Altair or Matplotlib for your data, and use Tesserax for your concepts.</p><p>Then, there is <strong>Tikz</strong>. (Again!) We already discussed it, but there is more.</p><p>On one hand, there is a strange Stockholm Syndrome in academia. We accept that drawing a simple three-state automata requires learning a bizarre macro language on top of another macro language (yeah, I&#8217;m looking at you, <em>LaTeX!</em>). We treat TikZ&#8217;s loop syntax like a revelation, ignoring that any programming language is actually better at this, but I can also have, I don&#8217;t know, <em>variables!</em> And also, who the hell knows how to write Tikz!? <em>(I know some of you nerds do, don&#8217;t give me that look. Shame on you.)</em></p><p>Tesserax bets that you don&#8217;t need a Domain Specific Language (DSL). You need objects. You need classes. You need type hints. If you want a grid of shapes? That is a nested list comprehension. If you want a recursive tree? That is a recursive function. You don&#8217;t need to &#8220;learn Tesserax.&#8221; You just need to know Python. By piggybacking on Python&#8217;s syntax, we get free IDE support, linting, and the ability to debug our drawings with the same tools we use to debug our kernels.</p><p>But also, even more important, the browser is the new print driver. TikZ was built for a world where the final output was a piece of paper. Tesserax is built for a world where the output is a responsive HTML page that <em>can</em> be printed.</p><p>By targeting SVG natively, we bypass the fragile conversion layers. We get semantic scaling. We get CSS styling. We stop fighting the renderer and start using the most optimized vector graphics engine in history: the modern web browser. And you can render SVG to PDF anytime. Going back, though, means you lose the <em>semantics</em> of what your drawing means.</p><p>And last but not least, Tesserax is <em>lean. </em>Like, zero dependencies. A couple hundred of pure, fully-typed, Python 3.12. Nothing more, nothing less. </p><p>I rest my case.</p><h2>What&#8217;s Next</h2><p>So Tesserax is my attempt to bring software engineering to scientific illustration. I&#8217;m releasing it today in a very crude, 0.2 something version, as I always do, to gather as much feedback as possible as soon as possible. From now on, all the diagrams in my papers, blog posts, and books will be built with this.</p><p>So, next step, I&#8217;m moving past the primitives phase now and into building high-level scientific abstractions. I want to generate automata that look like they were pulled from a classic textbook and data structures that reflect actual memory layouts. That&#8217;s what comes in the next few iterations. Moving from a world where we draw lines to a world where we code diagrams.</p><p>I&#8217;d love it if you&#8217;d give <strong><a href="https://github.com/apiad/tesserax">Tesserax</a></strong> a try. The documentation is still pretty thin but feel free to drop me a comment with any questions or suggestions.</p>]]></content:encoded></item></channel></rss>