<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://yev.bar/feed.xml" rel="self" type="application/atom+xml" /><link href="https://yev.bar/" rel="alternate" type="text/html" /><updated>2026-08-15T18:25:58+00:00</updated><id>https://yev.bar/feed.xml</id><title type="html">Yev Barkalov</title><subtitle>Here is the description I am putting in</subtitle><entry><title type="html">How to rewrite a codebase to a new language</title><link href="https://yev.bar/jq" rel="alternate" type="text/html" title="How to rewrite a codebase to a new language" /><published>2026-08-15T08:00:00+00:00</published><updated>2026-08-15T08:00:00+00:00</updated><id>https://yev.bar/jq</id><content type="html" xml:base="https://yev.bar/jq"><![CDATA[<h2 id="contents">Contents</h2>

<ul>
  <li><a href="#intro">Intro</a></li>
  <li><a href="#what-were-doing">What we’re doing</a></li>
  <li><a href="#why-we-can-do-this">Why we can do this</a></li>
  <li><a href="#where-to-start">Where to start</a></li>
  <li><a href="#preparing-the-engine">Preparing the engine</a>
    <ul>
      <li><a href="#authentication">Authentication</a></li>
      <li><a href="#environment">Environment</a></li>
    </ul>
  </li>
  <li><a href="#going-full-throttle">Going full throttle</a></li>
  <li><a href="#results">Results</a></li>
  <li><a href="#conclusions">Conclusions</a></li>
</ul>

<h2 id="intro">Intro</h2>

<p>Several weeks ago, the first <a href="https://github.com/oven-sh/bun/commit/9f3917e979fecd2dae1327e159c6b2fd258bb67f">sign</a> of Bun being rewritten from Zig to Rust was <a href="https://x.com/LukeParkerDev/status/2051431276205461635">spotted</a>. Starting as just an <a href="https://x.com/jarredsumner/status/2051595933704761618">experiment</a> before showing curious <a href="https://x.com/jarredsumner/status/2053047748191232310">results</a>, we then finally got the highly awaited <a href="https://bun.com/blog/bun-in-rust">blog post</a>. In the past, rewriting a codebase was a sign of either a bad design decision made in the beginning, or, a non-productive <a href="https://xkcd.com/356/">nerd snipe</a>. Today, the implied cost of engineering hours towards a rewrite is more affordable in tokens.</p>

<p><img src="/images/rewrite-meme.jpg" style="height: 500px; width: auto" /></p>

<h2 id="what-were-doing">What we’re doing</h2>

<p>Since rewriting a codebase should be doable now, how do we go about a large autonomous refactor? To show by example, we will be rewriting <strong>jq</strong> in <strong>Odin</strong> with ChatGPT 5.6 Luna.</p>

<details>
<summary>What is <b>jq</b>?</summary>

<p><a href="https://jqlang.org" target="_blank"><b>jq</b></a> is a CLI that's both simple and powerful for handling JSON. It supports both <a href="https://jqlang.org/manual/#basic-filters" target="_blank">elegant querying</a> as well as defining <a href="https://jqlang.org/manual/#defining-functions" target="_blank">reusable functions</a>.</p>

</details>

<details>
<summary>What is Odin?</summary>

<p><a href="https://odin-lang.org/" target="_blank"><b>Odin</b></a> is modern general purpose compiled language that has not yet gotten as much <a href="https://github.com/ghostty-org/ghostty" target="_blank">buzz</a> as Zig <a href="https://github.com/lightpanda-io/browser" target="_blank">has</a>. There are already <a href="https://github.com/itchyny/gojq" target="_blank">Go</a> and <a href="https://github.com/MiSawa/xq" target="_blank">Rust</a> implementations of <b>jq</b> hence picking an underappreciated language.</p>

</details>

<h2 id="why-we-can-do-this">Why we can do this</h2>

<p>jq has a language agnostic test suite which matches the same template as Bun.</p>

<div class="mermaid">
graph LR;
    subgraph Verification ["Test suite"]
        test_suite["Written to validate the app, not code"]
    end
    subgraph Application ["Codebase"]
        application["Written in some language"]
    end
    Verification--"Validates"--&gt;Application
</div>

<p>In non-technical terms, this is like defining a word in the dictionary without using the word in that sentence. Regardless of the specific language used, the purpose of the verifier (the test suite in the case of Bun or jq) is to give a strict thumbs up or down for some given code being “correct”.</p>

<div class="mermaid">
graph LR;
    subgraph Verification ["Test suite"]
        test_suite["Written to validate the app, not code"]
    end
    subgraph ValidApplication ["Working codebase"]
        application["Written in some language"]
    end
    subgraph InvalidApplication ["Not working codebase"]
        application2["Written in some language"]
    end
    Verification--"Looks good 👍"--&gt;ValidApplication
    Verification--"Not good 👎"--&gt;InvalidApplication
</div>

<p><strong>Note:</strong> It doesn’t matter whether or not the codebase was generated or artisanally written by a person, the purpose of the verification is the “good” or “not good”. If you’re on a team with a robust CI/CD, then seeing all green on a pull request is fair grounds for an “LGTM” and approval. Whether you view that as being a good or bad thing, the level of confidence in the result is what matters.</p>

<h2 id="where-to-start">Where to start</h2>

<p>Like how you could ask an LLM to prepare a local coding environment (fetching files, installing dependencies), you can ask the LLM to plan a larger codebase refactor before proceeding. Below is my summary of some LLM output on things about Odin that would be useful to know in advance of rewriting a C project.</p>

<ul>
  <li>Outlining the package graph such there are no import cycles</li>
  <li>No native coroutines or async behaviors</li>
  <li>No <a href="https://odin-lang.org/docs/faq/#does-odin-have-closures">closures that can access outside-scope values</a></li>
</ul>

<h2 id="preparing-the-engine">Preparing the engine</h2>

<p>Since we’re going to have a flexible number of agents working in parallel, we will also prompt the initial setup to have multiple files that prevent merge conflicts.</p>

<p><img src="/images/spongebob-fire-meme.jpg" style="height: 300px; width: auto" /></p>

<h3 id="authentication">Authentication</h3>

<p>For this project, I used <a href="https://vers.sh">Vers</a> VMs which basically give you git operations but for computers. First, create an authenticated Codex snapshot that we can restore for ephemeral agents. Authenticating a VM with Codex is as simple as going through the <strong>Device Code</strong> flow. After you’ve gotten the computer logged in to Codex, make a commit of that VM, and you’ll now be able to restore as many new VMs from this snapshot with your OpenAI account already signed in!</p>

<p>Aside from the security of delegating work to separate sandboxes or the space saved on my local laptop, Codex seems to have a limit of four parallel worktree agents on a given machine. Orchestrating these cloud computers was what allowed me to work on this in the timeframe I did.</p>

<h3 id="environment">Environment</h3>

<p>After this first setup, we need some simple plumbing such as GitHub PAT and preparing the Codex instance to be cloned when handling new prompts. After some basic branch protections, every PR would be reviewed by a short-lived agent (leaning on the point from the Bun blog post where an agent owning the PR would want for their changes to be merged whereas a blank review agent tends to be more unbiased)</p>

<div class="mermaid">
graph LR;
    subgraph Local ["Long lived agent"]
        LocalAgent["Locally running agent"]
    end
    subgraph Implement ["Short lived agent"]
        ShortLived1["Implementation agent #1"]
    end
    subgraph Implement2 ["Short lived agent"]
        ShortLived2["Implementation agent #2"]
    end
    subgraph Review ["Short lived agent"]
        ShortLived3["Review agent #1"]
    end

    Local--"Make change"--&gt;Implement
    Implement--"Ship feature branch"--&gt;Local
    Local--"Make change"--&gt;Implement2
    Implement2--"Ship feature branch"--&gt;Local

    Local--"Review"--&gt;Review
    Review--"Nit or LGTM"--&gt;Local
</div>

<p><strong>Author note:</strong> GitHub being down at points did cause nuisances and I did migrate some of the review/evaluation to the cloud VMs. The end result of 522 passing tests is still the same.</p>

<h2 id="going-full-throttle">Going full throttle</h2>

<p>“Now rewrite jq into Odin, make no mistakes”</p>

<div style="max-width: 350px">

<img src="/images/make-no-mistakes.png" style="height: 250px; width: auto" />

</div>

<p>Vibe coding is magical when you can tell a computer to make an app and it does. But, it’s much more useful when you give it the right <a href="https://arxiv.org/abs/2201.11903">direction</a>, <a href="https://mendral.com/blog/agent-harness-belongs-outside-sandbox">environment</a>, <a href="https://github.com/gastownhall/gastown">orchestration</a>, or even input (what are you telling the computer to do?). By lining up each of these in the setup, I’m then able to enter that prompt verbatim and know it should produce a meaningful result.</p>

<h2 id="results">Results</h2>

<p>After a bit more than a week since writing that last sentence in this blog post, I finally got a rewrite of jq into Odin based on their test suite!</p>

<p><a href="https://github.com/yevbar/jq-odin"><code class="language-plaintext highlighter-rouge">https://github.com/yevbar/jq-odin</code></a></p>

<p>I reminded ChatGPT at times to get “back on track” or use the cloud VMs instead of the count-limited local worktrees but I didn’t insert myself anywhere relating to the implementation of jq in Odin. As soon as ChatGPT completed the <em>Goal</em> (their version of a loop with Claude), it hid the banner showing the exact amount of time spent but it’s reported to be close to seven days and nine hours.</p>

<div style="max-width: 350px">

<img src="/images/goal-duration.png" style="height: 250px; width: auto" />

</div>

<p>For the cases strictly covered by the test suite, it gets the job done, however, there’s one caveat with this being a drop-in replacement for jq off the shelf. It strictly covers as much is expected by the test suite, which is only 522 test cases. While this may seem like a lot, test driven development tends to play well with language oriented projects (while I was working on <a href="/lsd-winding-down">LSD</a>, I had a slew of tests across the stack since there’s a broad surface area for a parser or interpreter).</p>

<p>For functionalities not covered by the test suite but only implemented in the CLI, there’s not a high certainty those capabilities would work in the Odin rewrite as it is. But, knowing <a href="https://vers.sh/blog/headless-browser-testing">agents can recursively improve</a>, it’d likely be a matter of implementation or orchestration to work on this such that the Odin-based jq would eventually have more feature parity.</p>

<h2 id="conclusions">Conclusions</h2>

<p>For <a href="https://github.com/itchyny/gojq">gojq</a> and <a href="https://github.com/01mf02/jaq">jaq</a>, other implementations of jq in different languages, there was weeks of time in between the start of those projects and their first working releases. While those projects seem to be focused on targeting the jq codebase instead of just the test suite gate, the results here further highlight that agentic coding can dramatically speed up work.</p>

<p>The partial result from the existing test suite alone may contribute to jq’s difficulty on <a href="https://programbench.com/task/jqlang__jq.b33a763/">ProgramBench</a> and why there are thousands of “generated behavorial tests”. If you’re wondering whether or not agents can write or rewrite real things, we’re there now. Whether or not agents can complete real things is a different question. It’s less what the team of agents is <em>made of</em> and more a question of what the team of agents is <em>doing</em>, you could have hundreds of “auto-researchers” but it means nothing if they’re as unproductive as a tech company trying out a “flat organization structure”.</p>

<p>Hack the planet!</p>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[Contents]]></summary></entry><entry><title type="html">Few days on Pokemon</title><link href="https://yev.bar/pokemon" rel="alternate" type="text/html" title="Few days on Pokemon" /><published>2026-07-10T08:00:00+00:00</published><updated>2026-07-10T08:00:00+00:00</updated><id>https://yev.bar/pokemon</id><content type="html" xml:base="https://yev.bar/pokemon"><![CDATA[<p>As part of research for the <a href="/magic">Magic project</a>, I came across the <a href="https://ptcg-abc.pokemon.co.jp/?lang=en">Pokemon AI Battle Challenge</a>. At the time, it looked like <a href="https://softwareengineering.stackexchange.com/questions/388092/what-exactly-is-yak-shaving">yak shaving</a> to go into it considering I didn’t play their TCG growing up. After wrapping up and shipping the Magic engine, I thought I was perfectly prepared to revisit the Pokemon competition. Like Magic, Pokemon is an incomplete information game. Unlike Magic, Pokemon doesn’t explode to the same degree of complexity and is simpler in a number of appreciable ways.</p>

<p>To compensate for joining a multi-month competition halfway through, I decided to timebox to a couple days and try approaching a generalized bot. Knowing Magic and understanding strategies to the game is what enabled me to hone in on stronger heuristic bots with the previous project. Given there are likely to be people who have that level of familiarity but for Pokemon, I know I’m faced with two options:</p>

<ul>
  <li>Study the game and the allowed cards to find a strong meta</li>
  <li>Learn just enough to make a functional bot and see if I can iterate on that</li>
</ul>

<p>While an LLM could do the first and I have seen that <a href="https://x.com/ditzikow/status/1922004651790000521">done before</a>, I didn’t trust my intuition for evaluating its output so I opted for the latter. I shipped a few bots that were able to play and win games but nothing I vibed broke the threshold for actually performing well against the field. Based on the result of the <a href="https://legendsofcodeandmagic.com">Legends of Code and Magic</a> annual competitions, most people there focused on heuristic bots that leaned on specific strategies or they applied neural networks to certain mechanics of the game. My suspicion is you could analyze the submissions in the Pokemon competition and find something similar.</p>

<p>So, right away, the Pokemon game deserves a kudos for not being immediately LLM’able. Perhaps if the AGI narrative holds, then benchmarks would be less measures of intelligence but more akin to post-quantum cryptography. Several years ago, quantum computing was more hype but now the <a href="https://csrc.nist.gov/Projects/Post-Quantum-Cryptography">US Government takes it seriously</a>. What post-quantum cryptography is about is technically just algorithms but they can also be understood as techniques for dealing with that strength of technology. Likewise, if AI on its own gets good at real world problems, we may benefit from some <a href="https://en.wikipedia.org/wiki/The_Hardest_Logic_Puzzle_Ever">“post-AI riddles”</a> that keeps AI in check like how gravity keeps us grounded.</p>

<p>As for takeaways, I think there are two I’d like to walk away with at least:</p>

<ul>
  <li>Friendshaped doesn’t mean friend: Data can meet a format but the model may not be representative of its behavior</li>
  <li>It’s better to be early and underdressed to a money making party than late: I can’t find the original tweet but it went along that idea.</li>
</ul>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[As part of research for the Magic project, I came across the Pokemon AI Battle Challenge. At the time, it looked like yak shaving to go into it considering I didn’t play their TCG growing up. After wrapping up and shipping the Magic engine, I thought I was perfectly prepared to revisit the Pokemon competition. Like Magic, Pokemon is an incomplete information game. Unlike Magic, Pokemon doesn’t explode to the same degree of complexity and is simpler in a number of appreciable ways.]]></summary></entry><entry><title type="html">Industrialization of Software</title><link href="https://yev.bar/industrialization" rel="alternate" type="text/html" title="Industrialization of Software" /><published>2026-07-07T08:00:00+00:00</published><updated>2026-07-07T08:00:00+00:00</updated><id>https://yev.bar/industrialization</id><content type="html" xml:base="https://yev.bar/industrialization"><![CDATA[<p>Everyone, including myself, has tried to invent some perspective or take on the current “AI bubble”. You could compare it to the recent crypto bubble where everyone seemed on board with this “technology of the future” but see it falls short in how crypto never broke out of its own “world”. DeFi only works in the world of crypto where AI is already used in different industries. You could compare it to the dot com bubble where any company with a dot com domain was IPO’ing at insane valuations. But, then see it falls short in how the internet was the first time people saw the widespread technological change happen in real time; consumers are aware of previous bubbles and not looking to have the AI hype be a exact repeat of the past. Any past “hype cycle” has a strong caveat which makes this AI phenomenon impossible to accurately compare without some “but really X was different” disclaimer.</p>

<p>However, a take I heard mid-conversation from someone who heard it from someone else stuck with me. What we’re seeing in the tech industry (especially with regard to programming roles thanks to coding agents) is similar to what happened to formerly Communist countries when they opened their borders after the collapse of the Soviet Union. It’s not a clean direct metaphor so, to deliver the point, let’s imagine we’re someone who works on some assembly line in a factory for a home-grown product.</p>

<p>The product you work on isn’t the greatest quality but it’s precious, it’s marked with the direction and instructions of the nation you’re producing value for. Your friends working in non-State-backed markets seem silly to you because they must cope in some way when their trade isn’t performing well. You’re smart for contributing to something the very nation you’re a part of says is worth something. Then, one day, that security and dependency disappears, democracy is trendy, and your neighbors can purchase from abroad rather than locally produced. While you protest consumer choice, you can’t help but notice your industry go belly up practically overnight.</p>

<p>Suppose, one day, North Korea stops being an authoritarian state and everyone who used to only buy from local manufacturers can buy from a neighboring Asian country. Assuming we’re past the “calibrating purchasing power with the rest of the world” phase, the majority of people would be expected to buy from abroad if it’s better or cheaper. Call it free market capitalism or competition, but the change in consumer behavior would be clear. Every SWE sitting with guilt or uncertainty over their future career is experiencing something similar to factory workers in a formerly shut-in nation that’s now opened up its borders.</p>

<p>If your immediate reaction is similar to when tech workers ask for a union, pity for techies is not the point of this post. Regardless of the average SWE salary to global poverty baselines, coding agents being able to replace technical talent is revealing of underlying problems similar to how COVID didn’t worsen institutions but made their problems clearer.</p>

<p>Back in the day, technical talent was as non-fungible as brand identities. If you were doing a venture-backed startup, you’d accumulate engineers to bring to life the vision in your pitch deck. It was a required dependency since, otherwise, who’d make the app? Nowadays, you can vibe the entire MVP with nothing more than a prompt or markdown file if you care that much. People would establish their careers on being specifically familiar with a certain programming language or framework (remember when bootcamps were shipping React or Angular developers?) but now that knowledge is only relevant insofar as it’s not immediately obvious to the models.</p>

<p>Whenever you were faced with a problem in the past, there was an actual tradeoff between finding something off the shelf or making it yourself (the classic “build vs buy” argument). Nowadays, people are able to make their own in-house solutions as soon as something off the shelf gives even the slightest inconvenience. While specific needs or “enterprise” use cases may require the existence of focused companies on certain solutions (ie Salesforce or Datadog), open source work or lower token costs help in the direction of people being able to build their own solutions. There was a time when it seemed crazy to leave bespoke hand-crafted books for the printing press. There was a time when it seemed crazy to leave fine cultery for plastic utensils. There was a time when it seemed crazy to leave intentionally made things for mass produced items. There was a time when it seemed crazy to leave physical media for the internet. The same will happen to software and how it gets engineered.</p>

<p>Other theories of the AI bubble generally describe how it will behave economically today and around the peak of the bubble, but they don’t always describe how the world will change afterwards. With the industrialization of software, seeing how consumers responded to the industrialization of mass produced goods, it paints a narrative for how generative UIs shift from being toys to real applications. An ordinary consumer looking for an item will go to Walmart or Amazon first before they look for a small local business that hand-crafts that said item. A UI that provides to users <em>just</em> what they need informationally makes a lot more sense than an app that was engineered for a world of capturing attention.</p>

<p>While apps are technically playing the same game as the “attention economy”, the goal is slightly different. Before, you wanted consumers or users to be viewing your displays to get the value you provided. Now, you want consumers or users to be getting the value you provide, regardless of the display[s] you may provide out of the box. MCP turns your buttons and forms into text questions and answers. Generative UIs turn your pages into partially renderable views for users to get no more than they’re interested in. You don’t care if a person pays you after messaging a chatbot or after clicking through a series of forms so long as your service is ultimately provided.</p>

<p>Some things do not change, of course. An app that’s trendy or popular will play the same today as it did in the past with word of mouth. Programs do ultimately depend on the value they’re providing and not just their aesthetic value. But, funnily enough, the vibe coders got it right with being able to view software as nothing more than an incidental problem for an AI to deal with, not people. People making toys don’t care about the exact chemicals used with the plastics, they just want a plastic toy in the end. Likewise, people solving problems with software will critique much less whether an app was developed in some language or another. I think people can care or not care what language an app was developed in. We may find a world where people care less about technical implementation details or a world where people actually care about programming languages like the environmental impact of certain culinary categories.</p>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[Everyone, including myself, has tried to invent some perspective or take on the current “AI bubble”. You could compare it to the recent crypto bubble where everyone seemed on board with this “technology of the future” but see it falls short in how crypto never broke out of its own “world”. DeFi only works in the world of crypto where AI is already used in different industries. You could compare it to the dot com bubble where any company with a dot com domain was IPO’ing at insane valuations. But, then see it falls short in how the internet was the first time people saw the widespread technological change happen in real time; consumers are aware of previous bubbles and not looking to have the AI hype be a exact repeat of the past. Any past “hype cycle” has a strong caveat which makes this AI phenomenon impossible to accurately compare without some “but really X was different” disclaimer.]]></summary></entry><entry><title type="html">Making an open source engine for Magic the Gathering in Python</title><link href="https://yev.bar/magic" rel="alternate" type="text/html" title="Making an open source engine for Magic the Gathering in Python" /><published>2026-07-02T05:00:00+00:00</published><updated>2026-07-02T05:00:00+00:00</updated><id>https://yev.bar/magic</id><content type="html" xml:base="https://yev.bar/magic"><![CDATA[<h2 id="contents">Contents</h2>

<ul>
  <li><a href="#the-gist">The gist</a></li>
  <li><a href="#what-makes-magic-particularly-hard">What makes Magic particularly hard?</a>
    <ul>
      <li><a href="#how-can-information-be-incomplete">How can information be incomplete?</a></li>
      <li><a href="#a-technique-for-incomplete-information">A technique for incomplete information</a></li>
      <li><a href="#turing-complete-whatnow">Turing complete whatnow?</a></li>
      <li><a href="#distinguishing-between-the-engine-and-the-bot">Distinguishing between the engine and the bot</a></li>
    </ul>
  </li>
  <li><a href="#how-other-people-have-hacked-magic">How other people have “hacked” Magic?</a>
    <ul>
      <li><a href="#official-products">Official Products</a></li>
      <li><a href="#java-based">Java-based</a></li>
      <li><a href="#magezero">MageZero</a></li>
      <li><a href="#deck-building-bot">Deck building bot</a></li>
      <li><a href="#simplified-engines">Simplified Engines</a>
        <ul>
          <li><a href="#legends-of-code-and-magic">Legends of Code and Magic</a></li>
          <li><a href="#open-mtg">open-mtg</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li><a href="#what-i-did">What I did</a>
    <ul>
      <li><a href="#frustrations-which-led-to-a-new-engine">Frustrations which led to a new engine</a></li>
      <li><a href="#how-i-vibed-the-engine">How I vibed the engine</a></li>
      <li><a href="#datalog">Datalog</a></li>
      <li><a href="#transpiling-english-to-datalog">Transpiling English to Datalog</a></li>
      <li><a href="#simple-api">Simple API</a></li>
      <li><a href="#bot">Bot</a></li>
      <li><a href="#vision-interaction">Vision interaction</a></li>
    </ul>
  </li>
  <li><a href="#results">Results</a></li>
  <li><a href="#what-i-did-with-my-account">What I did with my account</a></li>
  <li><a href="#github">GitHub</a></li>
</ul>

<h2 id="the-gist">The gist</h2>

<p>Months ago, I participated in a hackathon where the prompt was to make a programming language and then make a game in that language. There I <a href="https://x.com/itisyev/status/2002609926494171459">built a few DSLs specifically for card games</a>. Since then, I’ve been curious as to what it would look like or take to have a <a href="https://stockfishchess.org">Stockfish</a> for Magic and went through a tumultuous journey to put together the pieces I have. Hope you enjoy :)</p>

<h2 id="what-makes-magic-particularly-hard">What makes Magic particularly hard?</h2>

<p><a href="https://en.wikipedia.org/wiki/Magic:_The_Gathering">Magic the Gathering</a> is one of the most popular collectible card games and literally the only reason <a href="https://investor.hasbro.com/node/37856/html">the company that makes Monopoly is still alive</a>. Due to being an <em>incomplete information game</em> as well as one whose mechanics are <em>Turing complete</em>, it is one of the hardest games in the world.</p>

<h3 id="how-can-information-be-incomplete">How can information be incomplete?</h3>

<p>Unlike games like <a href="https://en.wikipedia.org/wiki/Deep_Blue_(chess_computer)">chess</a> or <a href="https://en.wikipedia.org/wiki/AlphaGo">Go</a>, Magic operates with <em>incomplete information</em> like <a href="https://ai.meta.com/blog/rebel-a-general-game-playing-ai-bot-that-excels-at-poker-and-more/">poker</a> or <a href="https://www.youtube.com/watch?v=oWdtSmpwFXY">mafia</a>. By “incomplete information”, there is knowledge relevant to the game not publicly available to all players. The simplest example of this would have to be rock-paper-scissors; <a href="https://www.youtube.com/watch?v=NgHvdCcmQ4o">knowing what an opponent is going to select</a> makes all the difference from the game being <a href="https://en.wikipedia.org/wiki/Nash_equilibrium">“solveable”</a> versus a disguised <a href="https://en.wikipedia.org/wiki/Monty_Hall_problem">Monty Hall problem</a>.</p>

<p>In the case of the Monty Hall problem, the “solution” can be determined by broadening out beyond what’s at face value. Let’s say you selected the first door and then learned behind the third door is a goat.</p>

<div class="mermaid">
graph LR;
    first_door["First door (Selected)"] --- second_door[Second door] --- third_door["Third door (Goat)"]
</div>

<p>This leaves us with two options: either the first door, or the second door.</p>

<div class="mermaid">
graph LR;
    first_door["First door (Selected)"] --- second_door[Second door]
</div>

<p>On first impression, this would lead one to think there’s a 50% chance of being right whether or not you swich. But, two <strong>options</strong> does not mean two equal <strong>possibilities</strong>. You have a 2/3 chance of choosing a door with a goat in the beginning, and then a 100% chance of winning when you switch (since you’d always be getting the car in that scenario).</p>

<p><img src="/images/monty-hall.png" style="height: 600px; width: auto;" /></p>

<h3 id="a-technique-for-incomplete-information">A technique for incomplete information</h3>

<p>Similar to the Monty Hall problem, with ReBeL, the expansion from hidden to public knowledge is the key technique. In the ReBeL paper, the authors suggest a version of rock-paper-scissors with a twist: whenever you win using scissors, you get two points and, whenever you lose with scissors, you lose two points.</p>

<p>Should we only consider what’s immediately in front of us (a player about to play a shape you don’t know), then there’s no reason to deliberate and you may as well see what goes.</p>

<p><img src="/images/rock-paper-scissors.png" alt="Diagram of blind rock-paper-scissors player from the ReBeL paper" style="width: 100%" /></p>

<p>If we consider the possible responses from the opponent player, then we can work out an optimal policy for how often to make certain plays. This is thanks to being able to weigh the different expected points or rewards from these different outcomes.</p>

<p><img src="/images/rock-paper-scissors-aware.png" alt="Diagram of aware rock-paper-scissors player from the ReBeL paper" style="width: 100%" /></p>

<p>With poker, you have <a href="https://pokercoaching.com/preflop-charts/">ranges</a>, the different probabilities of winning with different hands in different seats. The important detail about ranges is there is a finite number of probabilities to determine, whether or not you map exact permutations (ie a king of hearts with jack of spades) versus general hands (ie a <a href="https://www.pokerrrrapp.com/single-post/seven-deuce-bounty-rule-pokerrrr2">seven-deuce offsuit</a>). Unlike rock-paper-scissors or poker, there does not exist a finite ceiling to the amount of total information that could be contained in a game of Magic. This is thanks to it being <em>Turing complete</em> and there being an infinite amount of information that could emerge in a game.</p>

<h3 id="turing-complete-whatnow">Turing complete whatnow?</h3>

<p>Back in 2019, it was shown <a href="https://arxiv.org/abs/1904.09828">Magic itself could run a computer</a> like <a href="https://www.youtube.com/watch?v=jTZaUz8bYW8">a Redstone computer in Minecraft</a>. By “computer”, it was the simplest version of a computer (the <a href="https://en.wikipedia.org/wiki/Turing_machine">Turing machine</a>). How it ties into the complexity of a game has to do with the <a href="https://www.youtube.com/watch?v=macM_MtS_w4">Halting problem</a> which gives a limitation to what can be done with computers. For instance, with physics, you can’t run into a wall and expect to pass through. (<a href="https://medium.com/@shreyasmendhekar77/turing-machine-in-toc-dec838da2e7f">Source to below animation</a>)</p>

<p><img src="/images/turing-machine.gif" style="width: 100%" /></p>

<p>Unlike a game like chess or poker where players make one decision per turn (moving one piece in chess, a call or fold in poker), you can have a sequence of interactions in a single turn in Magic. Coupling this together with the <a href="https://mtgjson.com">large number of cards published</a>, you can end up with <a href="https://magic.wizards.com/en/news/making-magic/infinity-and-beyond-2002-11-04-0">infinite combos</a> that may or not ruin the friend group you’re playing with. Unfortunately, infinite storage does not exist so we can lean on a claim made by the original authors where the game itself may not be computably decidable but it may be <em>transition computable</em>.</p>

<p>They leave this as something they believe in more than they can formally prove given the 20,000+ corpus of cards at the time. However, I think it can be safely assumed given the following:</p>

<ul>
  <li>It’s possible to establish the static rules of the game for non-infinite situations</li>
  <li>It’s possible to define how cards and effects apply to each other for non-infinite situations</li>
  <li>There are <a href="http://mtg.icequake.net/www.crystalkeep.com/magic/rules/summaries/indexes/rule-general-421.php">rules for players to assign finite numbers to infinite loops</a></li>
</ul>

<p>While it does not rigorously cover all cases, the amount of gameplay around the world and work done by employees at Wizards of the Coast would leave me to think we’ve exhausted most of the obvious ones. Therefore, we should be able to have some software that handles moving from one state in the game to the next and not fret over the technical un-computability of it all.</p>

<p>As a final note on the Turing completeness, while <a href="https://www.youtube.com/watch?v=uNjxe8ShM-8">Powerpoint</a> and other games such as Minecraft do share the ability to have a computer run inside its environment, Magic is the only game that is both Turing complete <em>and</em> requires more than a single player by design. With this, it’s the closest thing to a <a href="https://defcon.org/html/links/dc-ctf.html">capture-the-flag</a> for people who choose fantasy over sci-fi.</p>

<h2 id="how-other-people-have-hacked-magic">How other people have “hacked” Magic</h2>

<p>Magic the Gathering has been around for <a href="https://www.youtube.com/watch?v=d7nAhLCKcZg">over 30 years</a> and, in that time, plenty of softwares have been built around and for the game. Below are some of those different products and projects:</p>

<h3 id="official-products">Official Products</h3>

<p><img src="/images/arena-product.png" style="height: 250px; width: auto" /></p>

<p><a href="https://magic.wizards.com/en/mtgarena">Arena</a> is the latest app distributed by Wizards of the Coast and it covers all the great stuff from cards to mechanics, and even <a href="https://mtg.fandom.com/wiki/Universes_Beyond">Universes Beyond</a>! While their first attempt at programming the game was <a href="https://www.reddit.com/r/magicTCG/comments/2jpjp2/if_you_really_are_frustrated_with_mtgo_stop_using/">infamously riddled with bugs</a>, Arena stands strong at millions of downloads plus players. In the game, they have a bot who’s available to play against named <a href="https://draftsim.com/sparky-decks-mtg-arena/">“Sparky”</a>. While not the strongest in performance, Sparky does act as their effective benchmark for a codified Magic player.</p>

<p>Relevant to one of the other projects and what I ended up building is Arena has a setting to <a href="https://draftsim.com/enable-detailed-logging-in-mtg-arena/">write game events to a log file in real time</a>. What this means is, rather than have to OCR the entire screen to get data relevant to game, you could parse predictable strings (this is also how <a href="https://untapped.gg/en">Untapped.gg</a> is able to “replay” historical games).</p>

<h3 id="java-based">Java-based</h3>

<p><img src="/images/java.png" style="height: 250px; width: auto" /></p>

<p>For a reason that’s not entirely clear to me, a lot of card game development in and outside of research uses Java. The two big contendors here in open source are <a href="https://github.com/Card-Forge/forge">Forge</a> and <a href="https://github.com/magefree/mage">Mage</a>. Both consist of engines to handle the game as well as UIs for local playing experiences. When I was attempting some early experiments, I found that its headless mode was not behaving in an actually “headless” mode without UI methods being discoverable in the stack trace. And so, running the engine in headless mode was like running a browser in <a href="https://developer.chrome.com/docs/chromium/headless">headless mode</a>, it may be running some less stuff but it kinda doesn’t work without the rendering parts.</p>

<p>Both Forge and Mage having this UI overhead (and Forge eating up my memory faster than an old person’s Alzheimer’s), ultimately contributed to my decision later on to develop a new engine. However, they do have extensive (or exhaustive depending on your views) heuristics already programmed so these two can also act as benchmarks for playing against.</p>

<h3 id="magezero">MageZero</h3>

<p><img src="/images/magezero.png" style="height: 250px; width: auto" /></p>

<p><a href="https://github.com/WillWroble/MageZero">MageZero</a> is, sadly, not a shipped bot or even an collection of bots. Instead, it’s a toolkit (remember when people wouldn’t shut up about <a href="https://github.com/langchain-ai/langgraph">LangGraph</a>?) for training your own deck-specific RL agents. The purpose behind this project is simple: different decks of cards affect the game as much as the game itself does. When you train a chess or Go bot, you’re always starting with the same pieces on the same board with the same rules. In Magic, there are cards that <a href="https://scryfall.com/card/c16/257/howling-mine">make people draw extra cards at the start of their turn</a> or <a href="https://scryfall.com/card/m14/35/silence">prevent players from casting any spells</a> so the cards involved in a player matchup matter a great deal.</p>

<p>While the implication of game-changing by cards is striking, it’s built on top of Mage which means it’s dependent on its Java-based engine; ergo another not-so-preferred target for me.</p>

<h3 id="deck-building-bot">Deck building bot</h3>

<p><img src="/images/deckbuilding-bot.png" style="height: 250px; width: auto" /></p>

<p>I found this through a <a href="https://news.ycombinator.com/item?id=29987714">comment on Hacker News</a> where a <a href="https://magic.wizards.com/en/formats/booster-draft">draft</a> player <a href="https://github.com/RyanSaxe/mtg">built a bot</a> to both choose cards during drafting as well as consolidate his final deck. While this did yield a comparable win rate to when he’s not using algorithms for deckbuilding, he was the one playing those games, not the bot.</p>

<p>Additionally, it’s unclear whether the <a href="https://scryfall.com/sets">sets</a> he trained against were the same as the ones in the drafts he played. Even in the case that it’s true, it still presents an interesting result to be able to map his preferences onto a neural network based on his historical gameplay (one may have expected a more intricate model to be needed for a game like Magic).</p>

<h3 id="simplified-engines">Simplified Engines</h3>

<h4 id="legends-of-code-and-magic">Legends of Code and Magic</h4>

<p><img src="/images/legends-of-code-and-magic.png" style="height: 150px; width: auto" /></p>

<p>For a couple years in a row, there was an <a href="https://arxiv.org/pdf/2305.11814">online competition</a> based on <a href="https://legendsofcodeandmagic.com">Legends of Code and Magic</a>, a card game similar to Magic but designed so that bot matches would be fair. By having a simpler game to work with, this meant more people with less compute would be able to participate as well. Over the years, various techniques from heuristics to neural networks have all been employed but either battling over marginal improvements or scoping each improvement into modular strategies that are plug-and-play.</p>

<h4 id="open-mtg">open-mtg</h4>

<p><img src="/images/open-mtg.png" style="height: 150px; width: auto" /></p>

<p>The <a href="https://github.com/hlynurd/open-mtg">open-mtg</a> project might be the closest thing to what I was looking for in the first place but it falls short in a few ways.</p>

<p>Firstly, the last commit was seven years ago and the game has <a href="https://www.youtube.com/watch?v=APEoxNtrnQU">changed quite a bit since then</a>. Secondly, it operates on a limited subset of the entire rules of the game which may or may not be misleading with respect to observed results (maybe there exists a ceiling for the number of rules that suffice in some given architecture in a non-obvious way).</p>

<h2 id="what-i-did">What I did</h2>

<h3 id="frustrations-which-led-to-a-new-engine">Frustrations which led to a new engine</h3>

<p>After reading papers and trying experiments, the one I’m most disappointed didn’t work was making a DSL for game strategies (imagine <a href="https://en.wikipedia.org/wiki/COBOL">COBOL</a> for describing patterns or plays) and then letting <a href="https://en.wikipedia.org/wiki/Neuroevolution_of_augmenting_topologies">NEAT</a> improve the underlying <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">syntactic graph</a> of a program. Maybe I didn’t add enough richness to the DSL or the NEAT implementation wasn’t granular enough but a lot of failures and crashes pointed to one common frustration: the Java-based engine was more than I needed and computationally expensive.</p>

<p>While Forge’s <a href="https://github.com/Card-Forge/forge/wiki/Card-scripting-API">DSL for cards</a> is neat, it exposes an inherent issue with keeping the engine up to date. In Magic, there are both rules and cards which may alter the game; all of these being relevant across an engine’s stack. Let’s take a look at what happens when a new set of cards is released with changes to the rules:</p>

<div class="mermaid">
graph LR;
    subgraph LR forge[Engine with UI and Card DSL]
      engine[Engine] --- ui[UI]
      engine --- dsl[Card DSL]
    end

    new_set[New cards] --&gt;|"New cards to apply"| engine
    new_set --&gt;|"New cards to present"| ui
    new_set --&gt;|"New cards to handle"| dsl

    changes_to_rules[Changes to rules] --&gt;|"Rules to be added/updated"| engine
    changes_to_rules --&gt;|"Changes to what/how is displayed"| ui
    changes_to_rules --&gt;|"Rules that affect how cards are handled"| dsl

    linkStyle 0 stroke-width:4px,stroke:red
    linkStyle 1 stroke-width:4px,stroke:red
    linkStyle 2 stroke:blue
	linkStyle 3 stroke:blue
	linkStyle 4 stroke:blue
	linkStyle 5 stroke:green
	linkStyle 6 stroke:green
	linkStyle 7 stroke:green
</div>

<p>New work needing to be done across the stack isn’t too terrible since there is a finite number of cards out there and only a finite number of new sets or rule changes happening each year. However, the intertwining of the pieces inside the engine does mean a “vibe refactor” like <a href="https://github.com/oven-sh/bun/pull/30412">what Bun did going from Zig to Rust</a> becomes a lot trickier. In fact, a “vibe refactor” for either of the Java engines would be more <a href="/exploring-programbench">non-trivial</a> since, unlike Bun, the test suites are written in the same language as the project.</p>

<p>However, building a new engine with a focus on performance would help in a number of ways. First, it’d help wrestle with the question of “if we looked far enough ahead, could we generally find wins?”. Second, it’d only use the compute necessary for games so training models on more data (synthetic or self-played) becomes more realistic.</p>

<h3 id="how-i-vibed-the-engine">How I vibed the engine</h3>

<p>So, if not a refactor, I need some way to vibe code a new engine (there are tens of thousands of cards out there and I am but one mere mortal). Each time I was reviewing a possible solution for verification to check agent output, the pattern I was looking for was: take the latest rules (which are available <a href="https://magic.wizards.com/en/rules">on the Wizards of the Coast website as TXT or DOCX</a>), loop over each rule and convert it to some spec, append to our verification tool (building up a test suite rule by rule).</p>

<div class="mermaid">
graph LR;
    rules[Get latest rules] --&gt; next_rule[Get next rule]
    next_rule --&gt; translate[Translate to spec]
    translate --&gt; add[Append to verification to eventually be used by agents]
    add --&gt; next_rule
</div>

<p>At one point, I was looking into preparing <a href="/firebird">another swarm</a> with <a href="https://en.wikipedia.org/wiki/Formal_verification">formal verification</a> like <a href="https://github.com/tlaplus">TLA+</a> to verify the behavior in the same way that integration test suites are used for verifying “vibe refactors”. The one I had the most hope for was <a href="https://dafny.org">dafny</a> since it <a href="https://dafny.org/v3.10.0/DafnyRef/integration-py/IntegrationPython">officially outputs to Python</a>. However, it wasn’t clear if the language would be able to represent enough logic to handle the rules relating to Magic. If involved, the purpose of the verification piece is to be the checkbox that matters with respect to completion, otherwise, it may as well be AI psychosis to think anything succeeded.</p>

<p>But then, another realization sunk in: however I’m interpreting the text in the rules to be converted into a formal verification language is in of itself going to be easily argued or prone to faults. Then, I remembered that rules are privy to change for interpretation, an example being when a player named a card that was not the one on the board but understood to be implicitly (<a href="https://articles.starcitygames.com/articles/on-bannings-and-coverage/">“Borborygmos Incident”</a>). Arguments over the <a href="https://www.law.virginia.edu/scholarship/publication/lawrence-b-solum/953451">US Constitution</a> aren’t about the content itself so much as they’re about the <em>interpretation</em> of said content. Given this as well as the general complexity in Magic, it seemed like the answer could be simpler:</p>

<div class="mermaid">
graph LR;
    rules[Get latest rules] --&gt; interpret[Interpret into engine code]
    cards[Get latest cards] --&gt; interpret
</div>

<p>Instead of architecting a new engine with its own complexity and costs as the game expands, boiling down the game into a means of interpreting the English texts from the rules and cards makes it more adaptable to changes to the game (which must be somewhat legalese given millions play the game and some compete for millions). Maintaining this “English interpreter” is less a problem with a codebase and more reviewing the interpretation of grammatical patterns in texts.</p>

<div class="mermaid">
graph LR;
    rules[Get latest rules] --- r_can_interpret[Can interpret based on the last grammar]
    rules --- r_cannot_interpret[Cannot interpret based on the last grammar]
    cards[Get latest cards] --- c_can_interpret[Can interpret based on the last grammar]
    cards --- c_cannot_interpret[Cannot interpret based on the last grammar]

    r_can_interpret --&gt; interpret[Interpret into engine code]
    c_can_interpret --&gt; interpret

    r_cannot_interpret --&gt; to_maintain[Incrementally novel English grammar is used]
    c_cannot_interpret --&gt; to_maintain
</div>

<p>Which makes the choice of output language more significant. If they were more functionally complete, I’d have gone for the DSLs I made at the hackathon months ago, which were solely for this type of problem. There is, fortunately, a solution that not only follows what’s been used historically with games academically but <em>also</em> offers a C++ build in the end (making an integration with Python similar to how PyTorch uses Python to drive a Torch C++ backend).</p>

<h3 id="datalog">Datalog</h3>

<p>In the world of academic work relating to games, there is a tool used by the name of <a href="https://en.wikipedia.org/wiki/Game_Description_Language">Game Description Language</a> (or GDL for short) which is a variant of <a href="https://en.wikipedia.org/wiki/Datalog">Datalog</a>. While it was originally developed in the pursuit of general game playing, its base language is suited rather well for what we’re looking for. Unlike its parent language, <a href="https://en.wikipedia.org/wiki/Prolog">Prolog</a> (which was used to write the first version of Erlang!), Datalog is <em>not</em> Turing complete. You never have to worry about “what if this goes off to infinity?”. This is thanks to the underlying <a href="https://x775.net/2019/03/18/Introduction-to-Datalog.html">bottom-up architecture</a> where it builds up truths from known facts in contrast to Prolog which will start with a query and then inquire into its truth-iness based on established rules.</p>

<p>Following the end of the <a href="#turing-complete-whatnow">Turing complete section</a> where I describe Magic as being “transition computable” but not “game computable”, there cannot exist a deterministic program that accepts a Magic game in its entirety and strictly computes whether or not the game ends. But, there can be a program that would accept a game and tell you what happens next. This is what we would look for in an <em>engine</em> that describes the state of the game in order to provide to a <em>bot</em> which drives decisions according to wherever the engine is currently at.</p>

<p>Another variant of Datalog, <a href="https://souffle-lang.github.io">Souffle</a>, is the one I’m using and it also happens to have been used to <a href="https://souffle-lang.github.io/applications">find vulnerabilities in the Java JDK plus used by Amazon to verify their VPN connections</a>. To remedy changes to larger game states (ie late in the game or a large number of creatures are in play), I had Claude translate the work from a <a href="https://souffle-lang.github.io/ppdp21.html">2021 paper and branch</a> to a fork of Souffle. This is so our engine can handle editing the state versus refreshing from scratch and being able to discern which case is more optimal. This is useful for when a single creature kill does nothing versus when a single creature kill requires untangling a bunch of effects.</p>

<h3 id="transpiling-english-to-datalog">Transpiling English to Datalog</h3>

<p>There is an old fashioned way of looking at sentences called the <a href="https://en.wikipedia.org/wiki/Reed-Kellogg_sentence_diagram">Reed-Kellog sentence diagram</a> where you deconstruct a sentence into a tree where the branching describes structure and modifiers.</p>

<p><img src="/images/reed-kellog.jpg" style="width: 100%" /></p>

<p>Programmatically, there are tools that provide <a href="https://stanfordnlp.github.io/CoreNLP/depparse.html">“dependency parsing”</a> where you take in some text and get back a graph like the ones shown above. The one I used is <a href="https://spacy.io/usage/linguistic-features">spaCy</a> and, once you have a graph containing relations among words or tokens, then you have an <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">abstract syntax tree</a>!</p>

<p>Why this is powerful is programming languages from Rust to Odin all under the hood start by taking the source code (which is really just text), shuffling it around a graph structure, then finally producing your output. As an example in our application, let’s consider the following rule:</p>

<blockquote>
  <p>“If a creature has toughness 0 or less, it’s put into its owner’s graveyard.”</p>
</blockquote>

<p>Right away, we know this is a conditional with the “If” at the beginning. We can prepare the conclusion “it’s put into its owner’s graveyard” as the effect from satisfying what is entailed in the condition clause. Here the only real value that matters is the “creature” and it having the “toughness” that is, in this case, “0 or less”, which maps to <code class="language-plaintext highlighter-rouge">&lt;= 0</code>. One comment about the below diagram is the “O”s down in the final Souffle are uppercase o’s and not zeros (which have <a href="https://en.wikipedia.org/wiki/Slashed_zero">zero fills</a>), this is the convention for referring to “object” variables in Souffle.</p>

<p><img src="/images/spacy-deps.png" style="width: 100%" /></p>

<p>Souffle being a <a href="https://unplannedobsolescence.com/blog/prolog-basics-pokemon/">logic oriented programming language</a> also makes aspects of encoding the game rather neat, such as expressing some rules as assertions rather than a chain of conditions and function calls.</p>

<p>As a caveat, I tried to keep it faithful to this architecture, albeit some subagents wrote in some straight regex replacements as the “intrepreting”. As funky as that choice was, I did some refactoring to backtrack and tidy up the English-to-Souffle pipeline. Ideally, regex would be optionally used structurally (ie replacing URLs with strings to get picked up as a single word by a parser) before being fed into the more grammatically aware English-to-Souffle pipeline more utilizing spaCy.</p>

<h3 id="simple-api">Simple API</h3>

<p>One of the first things I had wanted when I started looking into a “Stockfish for Magic” was there being something similar to <a href="https://python-chess.readthedocs.io">python-chess</a> where you could install as easily as:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>pip <span class="nb">install </span>chess
</code></pre></div></div>

<p>Then play like so:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Import the library
</span><span class="kn">import</span> <span class="nn">chess</span>

<span class="c1"># Create a board (you need one to play a game of chess)
</span><span class="n">board</span> <span class="o">=</span> <span class="n">chess</span><span class="p">.</span><span class="n">Board</span><span class="p">()</span>

<span class="c1"># First move e4
</span><span class="n">board</span><span class="p">.</span><span class="n">push_san</span><span class="p">(</span><span class="s">"e4"</span><span class="p">)</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">mtg</code> and a couple other good names were already taken so I went for <code class="language-plaintext highlighter-rouge">python-mtg</code> for simplicity sake. Now you can install as simply as:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>pip <span class="nb">install </span>python-mtg
</code></pre></div></div>

<p>Then play like so:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Import the library
</span><span class="kn">from</span> <span class="nn">mtg</span> <span class="kn">import</span> <span class="n">Game</span><span class="p">,</span> <span class="n">mountain</span>

<span class="c1"># Create a game (you need players and cards to play a game of magic)
</span><span class="n">game</span> <span class="o">=</span> <span class="n">Game</span><span class="p">.</span><span class="n">new</span><span class="p">(</span>
  <span class="p">[</span><span class="n">mountain</span><span class="p">]</span> <span class="o">*</span> <span class="mi">40</span><span class="p">,</span> <span class="c1"># Every player has a basic deck
</span>  <span class="n">starting_hand</span><span class="o">=</span><span class="k">lambda</span><span class="p">:</span> <span class="p">[</span><span class="n">mountain</span><span class="p">],</span> <span class="c1"># Every player has at least a Mountain in their starting hand
</span><span class="p">)</span>

<span class="c1"># Play a land
</span><span class="n">game</span><span class="p">.</span><span class="n">play</span><span class="p">(</span><span class="n">mountain</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="bot">Bot</h3>

<p>For making the bot, I set up a <a href="https://github.com/yevbar/witchcraft/blob/master/packages/mtg/players.py"><code class="language-plaintext highlighter-rouge">Player</code> class</a> that would make it easier to define and read programmatic Magic players. Rather than approach defining bots as an implementation problem, this helps heuristics be more written like how you would instruct someone to play your deck (ie when a friend borrows your cards to play a game). It also enables the below bot where all it does is play lands, cast spells, swing with everything, and never blocks.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">mtg</span> <span class="kn">import</span> <span class="n">Player</span><span class="p">,</span> <span class="n">PriorityOption</span> <span class="k">as</span> <span class="n">Do</span>

<span class="k">class</span> <span class="nc">BlindAggroPlayer</span><span class="p">(</span><span class="n">Player</span><span class="p">):</span>
  <span class="k">def</span> <span class="nf">choose_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">game</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">game</span><span class="p">.</span><span class="n">prioritize</span><span class="p">(</span>
      <span class="n">Do</span><span class="p">.</span><span class="n">LANDS</span><span class="p">,</span>
      <span class="n">Do</span><span class="p">.</span><span class="n">SPELLS</span><span class="p">,</span>
      <span class="n">Do</span><span class="p">.</span><span class="n">ATTACKS</span><span class="p">,</span>
      <span class="n">Do</span><span class="p">.</span><span class="n">SKIP</span>
    <span class="p">)</span>
</code></pre></div></div>

<p>Or, to have more calculated preferences for different decision paths (ie if you were to MCTS):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">mtg</span> <span class="kn">import</span> <span class="n">Mover</span><span class="p">,</span> <span class="n">Player</span><span class="p">,</span> <span class="n">PriorityOption</span> <span class="k">as</span> <span class="n">Do</span>

<span class="k">class</span> <span class="nc">HeuristicPlayer</span><span class="p">(</span><span class="n">Player</span><span class="p">):</span>
  <span class="k">def</span> <span class="nf">choose_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">game</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Move</span> <span class="o">|</span> <span class="bp">None</span><span class="p">:</span>
    <span class="bp">self</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="n">game</span><span class="p">)</span>  <span class="c1"># refresh self.creatures, self.opponent, self.life
</span>
    <span class="k">return</span> <span class="n">game</span><span class="p">.</span><span class="n">prioritize</span><span class="p">(</span>
      <span class="n">Do</span><span class="p">.</span><span class="n">LANDS</span><span class="p">.</span><span class="n">prefer</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">land_choice</span><span class="p">),</span>

      <span class="n">Do</span><span class="p">.</span><span class="n">RESOLVE_TRIGGER</span><span class="p">.</span><span class="n">prefer</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">resolve_choice</span><span class="p">,</span> <span class="n">floor</span><span class="o">=</span><span class="mf">0.0</span><span class="p">),</span>
      <span class="n">Do</span><span class="p">.</span><span class="n">SPELLS</span><span class="p">.</span><span class="n">prefer</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">develop_choice</span><span class="p">,</span> <span class="n">floor</span><span class="o">=</span><span class="mf">0.0</span><span class="p">),</span>
      <span class="n">Do</span><span class="p">.</span><span class="n">ABILITIES</span><span class="p">.</span><span class="n">prefer</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">develop_choice</span><span class="p">,</span> <span class="n">floor</span><span class="o">=</span><span class="mf">0.0</span><span class="p">),</span>

      <span class="n">Do</span><span class="p">.</span><span class="n">ATTACKS</span><span class="p">.</span><span class="n">prefer</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">attack_choice</span><span class="p">),</span>
      <span class="n">Do</span><span class="p">.</span><span class="n">BLOCKS</span><span class="p">.</span><span class="n">prefer</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">block_choice</span><span class="p">),</span>
      <span class="n">Do</span><span class="p">.</span><span class="n">SKIP</span><span class="p">,</span>
    <span class="p">)</span>
</code></pre></div></div>

<p>Or, my personal favorite, being able to <a href="https://github.com/yevbar/witchcraft/blob/master/packages/mtg/lookahead.py">search for a winning path and taking it</a> (verified against Forge with turn-1 kills). While the test of being able to perform a <a href="https://scryfall.com/card/thb/73/thassas-oracle">Thassa’s Oracle</a> win is forced with a certain starting hand, solving the problem of finishing a game is often the last problem in minmaxing before optimizing or pruning tree search.</p>

<h3 id="vision-interaction">Vision interaction</h3>

<p>It’s easy to assume that wrangling an entire screen with OCR would be difficult and that’s generally correct. However, we can take advantage of something from web scraping. If we took a look at the home page for Hacker News and ask how to get the titles of the top posts.</p>

<p><img src="/images/hackernews.png" style="width:100%" /></p>

<p>Extracting the text itself from this screen would be a challenge but if we note that each of the elements of interest match the CSS selector <code class="language-plaintext highlighter-rouge">span.titleline</code> then we can go from that string to the strings we’re interested in.</p>

<p><img src="/images/hackernews-highlighted.png" style="width:100%" /></p>

<p>By going bottom-up we’re able to go from the substance to the content of interest. In the case of Arena where there are screens consisting of a lot of text.</p>

<p><img src="/images/arena.png" style="width:100%" /></p>

<p>The thing we’re then more interested in are the general shapes appearing on the screen (ie orange or blue buttons) rather than trying to find the button that corresponds to a “Play” or “Pass” action. As such, automation here can be the effect of going from content to substance.</p>

<p><img src="/images/arena-highlighted.png" style="width:100%" /></p>

<p>I use <a href="https://moondream.ai">Moondream</a> for this and it improves the problem from <a href="https://xkcd.com/1425/">impossible</a> to somewhat dependable similar to spaCy. A necessary disclaimer is both of these tools under the hood are stochastic models and therefore should not be treated as formalizations but applicable tools.</p>

<h2 id="results">Results</h2>

<p>After getting a minimal working setup to connect the “blind aggressive” bot defined at the top of <a href="#bot">the bot section</a> to some simulated interactions, I was able to let it drive my seat autonomously and beat Arena’s Sparky following those minimal heuristics (play lands, cast spells, swing with everything, never block).</p>

<p>Following that, I began working on extending the simulated interactions further for a heuristic bot that can target cards (so blocking can be translated from the engine to Arena) and got it to the point where it drove its own seat autonomously and beat Sparky three times in a row. The cards chosen for that white life-gain deck were intentional so it had an easier time with targeting; call it convenience or laziness but it got the job done.</p>

<iframe width="420" height="315" src="https://www.youtube.com/embed/fudgK4cj-QE">
</iframe>

<p>I then began iterating on a more improved heuristic player to drive a deck I had successfully played with against others online (mono-red with <a href="https://scryfall.com/card/om1/76/electro-assaulting-battery">Bayo, Irritable Instructor</a> as the commander). On more than one occassion, it would drive the seat through 10+ moves and hit a snag with a new interaction with Arena that was not yet handled (ie warp casting), I’d then take over the seat only to win using actions already validated with the bot (ie it could cast spells targeting creatures owned by the opponent or myself).</p>

<p>While the cards in my red deck were both competitive as well as annoying (some opponents may resign out of hinderance rather than surrender), the end of the third game in the video shows the bot ignoring casting any spells and goes straight into combat since its lookahead identified a path to win the game. This validates the player framework being able to both develop a board following established heuristics, <em>and</em> finish a game to completion when a winning sequence of moves is identified.</p>

<p>Had I modified the player evaluation to always load in the heuristics before making a move, then it could have had a similar developer experience to <a href="https://sonic-pi.net">live coding</a> except for Magic (where I could edit the file and the player’s strategy updates in real time). But, after a couple cycles of setting up a game, letting the bot take over and drive my seat, and hitting more snags with missing interactions, it felt less like I was validating the tech as much as I was developing a cheat software. Since that’s not my interest here, I decided to let my work reach a halting point to tidy up the code and put together this write-up.</p>

<p>The latest <code class="language-plaintext highlighter-rouge">inthearena</code> work may be buggy or not even working and I’d think that’s possibly for the best to be that way. Sharing nonetheless for the sake of sharing work.</p>

<h2 id="what-i-did-with-my-account">What I did with my account</h2>

<p>Did I feel bad about doing this project with no transparency about being a bot? Yes. I tried setting up a new account solely for doing this but I already had built up a collection of cards in my first account and the onboarding took forever. Having played Magic since middle school, the game has a special place in my heart so I certainly wasn’t the most proud to be ruining a potentially fun experience for other players. At the same time, playing any game online does entail a bit of figuring out how natural or artificial your opponent’s gameplay is.</p>

<p>If you’re reading this, I’ve already submitted a ticket to delete my Wizards account and I have no interest in abusing this software (I also have already spent enough time playing the game, kudos to the folks who made it). By sharing publicly, I hope to share both content with folks who’d be interested in the engine work I did as well as potentially to folks working at Wizards of the Coast so they maybe have an example of what to block with anticheat work.</p>

<p>My hope is that Wizards of the Coast does not remove the detailed logging setting or delay it by some time window (1), but, instead, sees this as a new API or game in of itself. Lichess used to have <a href="https://lichess.org/team/lichess-bots/tournaments">bot tournaments</a> and there are <em>technically</em> <a href="https://codecombat.com">games you can code</a> but these usually tend to be simple games or knockoffs from real ones. Universes Beyond can happen because the IP they borrow (ie Marvel, Doctor Who, Lord of the Rings) don’t have an involvement with card games (2). As such, I think even a separate tournament could be neat (don’t repeat Legends of Code and Magic where it’s just a research project, put up a small cash prize to attract attention).</p>

<p>Thank you for reading and, as always, <a href="https://en.wikipedia.org/wiki/Hackers_(film)">hack the planet!</a></p>

<p>(1) A few-minute delay to the detailed log output would prevent it from being usable for automated gameplay but allow it to still be used by services like Untapped.gg</p>

<p>(2) Otherwise, I’d have really wanted to see a <a href="https://www.youtube.com/watch?v=eY-W9gmwxhg">One Piece</a> theme</p>

<h2 id="github">GitHub</h2>

<p><a href="https://github.com/yevbar/witchcraft">https://github.com/yevbar/witchcraft</a></p>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[Contents]]></summary></entry><entry><title type="html">Software as Commodity</title><link href="https://yev.bar/software-as-commodity" rel="alternate" type="text/html" title="Software as Commodity" /><published>2026-06-10T08:00:00+00:00</published><updated>2026-06-10T08:00:00+00:00</updated><id>https://yev.bar/software-as-commodity</id><content type="html" xml:base="https://yev.bar/software-as-commodity"><![CDATA[<p>Not too long ago I went to a fireside between industry veterans with opinions about devtools and open source. During the conversation, one of the two posed a thought to the audience to consider a world in which “software is commoditized”. When you call a car on Uber, you only care where the car is; so what if you could generate a UI on the fly showing you just the location and ETA instead of what we have today with all the buttons and distractions? Mind you, this was from the same person who, in response to a question about <a href="https://www.linkedin.com/posts/malwaretech_some-quick-thoughts-on-claude-mythos-and-ugcPost-7448887241151520768-5aV8/">what happens when VC capital stops subsidizing tokens</a>, chortled that people will pay nonetheless. At the time, it gave off more of an impression of “I’m wealthy and enjoying it” (he works at a big name firm), but with what coding agents can do today plus what they’d be able to accomplish down the line, it doesn’t seem so far stretched.</p>

<p>From the burnout of <a href="https://x.com/saranormous/status/2064510215056400652">AI psychosis</a> to the ennui from <a href="https://darioamodei.com/essay/machines-of-loving-grace">software engineering becoming irrelevant</a>, it’s easy to be someone working in tech or another impacted skill area (not industry) and feel like we can just scroll through <a href="https://theprint.in/feature/young-south-koreans-burnout-loneliness-anxiety/2955246/">websites</a> engineered to our <a href="https://ai.meta.com/blog/tribe-v2-brain-predictive-foundation-model/">dopamine</a> while we sit and wait for UBI (or UHI, take your pick). But, this is the same tune of what we said about self driving cars where, a decade ago, we were told people driving would <a href="https://www.vox.com/2016/9/18/12955162/lyft-gm-self-driving-cars">become meaningless in five years</a>.</p>

<p>What if I told you the commodification of software is an inevitable conclusion to a story that’s been building for <em>years</em>? Let’s begin with the opening sentence from geohot’s <a href="https://geohot.github.io/blog/jekyll/update/2026/05/24/the-eternal-sloptember.html">Sloptember post</a>:</p>

<blockquote>
  <p>I’m calling it now, the adoption of AI agents into software development will be one of the most costly mistakes in the field’s history.</p>
</blockquote>

<p>If you’re skeptical of coding agents, you’d probably agree with the assertion and are waiting for people to stop wasting money on LLMs. If you’re fully surfing the vibes, then you may think he’s “just not using it correctly”. However, let me suggest to you a view that’s not skeptical of coding agents while agreeing with the sentence. The previous event before we commodified <em>software</em> was when we commodified <em>software talent</em>. By this, I don’t mean recruiting but, rather, coding bootcamps.</p>

<p>The sales pitch for coding bootcamps is simple: you spend less time studying than you would on a computer science degree but get the same job in the end. At one point, there was a small <a href="https://www.youtube.com/playlist?list=PLtuWfrF8FU5y543ZsGKIxIprtFlbf9mQE">frenzy</a> and some would even grant a <a href="https://en.wikipedia.org/wiki/Make_School">Bachelor’s degree</a> upon completion. Someone who prided themselves on being one of the nerds who sat in the back of the classroom and studied for years to get ahead of their peers may view this as a saturation of software engineering talent. However, considering software engineers are still people and you’ll have high performers as well as low performers, all it did was add more domestic labor. Coupling this with <a href="https://www.terminal.io/">devshops</a> that just trebuchet engineers at problems (which only saw increased demand through the 2010s as <a href="https://a16z.com/why-software-is-eating-the-world/">software was eating the world</a>), we were getting closer to a world where <a href="https://web.archive.org/web/20211006104415/https://handbook.sourcegraph.com/company/strategy">more people would be able to code</a>.</p>

<p>If, before, your problem was you couldn’t find a person who knew how to code, now there’s a whole crowd who got the skills you need. However, like why we don’t only hire remote workers for a <a href="https://www.levels.fyi/t/software-engineer/locations/india">fraction of the salary</a>, domain expertise can matter as much as high/low performance. Anyone who’s worked on a project with others knows people have strengths and weaknesses; a person who’s awesome at writing SQL queries may be allergic to frontend development whereas the one who’s great with CSS animation transitions may roll their eyes back at the idea of a database. Just throwing money at technical labor without clarity of the goal is as effective as waiting for monkeys on typewriters to hand you Shakespeare.</p>

<p>At the same time, like how <a href="https://x.com/Bouazizalex/status/2020159203382550530">all large companies are remote companies</a>, all software practices can be <a href="https://en.wikipedia.org/wiki/Systems_development_life_cycle">codified</a> and we can assign agents to the responsibilities that comprise an engineering org. Economically, it may be the case that smart people orchestrate systems and spend a fraction of what hiring a team would cost but it may also be the case (as practices are figured out and token prices stop being subsidized) that we end up spending more in total with “artificial talent” than if we had hired a team of people.</p>

<p>As software products continue to become further industrialized, holding opinions such as “LeCunn is right about LLMs” only gets a part of the picture right. Like Warren Buffet’s remark on how he’s “never met a rich economist”, something similar could be said about there not being a <em>directly</em> impactful theorist (emphasizing directly since Marx is what the communists read but Lenin and Trotsky were the ones who directed impact). It is true software engineering is experiencing a <a href="https://mastrojs.github.io/blog/2026-05-23-is-AI-causing-a-repeat-of-frontends-lost-decade/">deskilling</a> like frontend development, but, it doesn’t change how the internet promises all of humanity’s knowledge yet plenty of dumb people still exist. Just because AI is <em>capable</em> of doing things doesn’t mean it will, someone will still need to invoke it or prepare a system that does so.</p>

<p>Unfortunately, if industrialization and commodification has taught us something (ie mass printing of books, manufacturing and assembly lines), it’s that people will broadly value convenience or accessibility over something more bespoke (why buy a single knife when you can get the whole cutlery set). We could ruminate in that and say this is what people are actually worried about when it comes to AI replacing software engineers. Although, it would be the same as trying to make money with an investment thesis that the world is going to end; buying gold makes no sense and you’re just hoarding bananas. Instead, this should be seen as a dawn similar to that of the internet when everyone online suddenly got the superpower of reading from and communicating with others across the world.</p>

<p>There are numerous problems in math that get shelved or put up with a bounty because the person putting up the problem does not have the capacity or ability to tackle that problem. In the world of software, there have been numerous ideas that wouldn’t exist because there wasn’t any technical effort put in that direction (ie when your friend says “what if there was an app that could X?”). Now, we can technically make anything and delegate our focus more towards the design of the product. In the words of <a href="https://x.com/MeekMill/status/2064352426371534875">Meek Mill</a>:</p>

<blockquote>
  <p>I feel like they can make my claude smarter who can help em do that … or what is the smartest ai program available to the people? Because the things I am learning in a week would take me 5 years to learn.</p>
</blockquote>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[Not too long ago I went to a fireside between industry veterans with opinions about devtools and open source. During the conversation, one of the two posed a thought to the audience to consider a world in which “software is commoditized”. When you call a car on Uber, you only care where the car is; so what if you could generate a UI on the fly showing you just the location and ETA instead of what we have today with all the buttons and distractions? Mind you, this was from the same person who, in response to a question about what happens when VC capital stops subsidizing tokens, chortled that people will pay nonetheless. At the time, it gave off more of an impression of “I’m wealthy and enjoying it” (he works at a big name firm), but with what coding agents can do today plus what they’d be able to accomplish down the line, it doesn’t seem so far stretched.]]></summary></entry><entry><title type="html">Exploring ProgramBench</title><link href="https://yev.bar/exploring-programbench" rel="alternate" type="text/html" title="Exploring ProgramBench" /><published>2026-05-22T08:00:00+00:00</published><updated>2026-05-22T08:00:00+00:00</updated><id>https://yev.bar/exploring-programbench</id><content type="html" xml:base="https://yev.bar/exploring-programbench"><![CDATA[<h2 id="prelude">Prelude</h2>

<p>Meta recently <a href="https://arxiv.org/abs/2605.03546">published</a> a <a href="https://programbench.com/">benchmark</a> named ProgramBench where the goal is simple: replicate a project without the <a href="https://programbench.com/#faq-internet">internet</a> (more details in <a href="https://programbench.com/blog/is-programbench-impossible/">their blog post</a>). In contrast to <a href="https://github.com/oven-sh/bun/pull/30412">rewriting</a> from one language to another, this benchmark for rewriting projects without source code comes at a funny time. Extending the idea that engineers will be <a href="https://darioamodei.com/essay/machines-of-loving-grace">replaced in a couple years</a>, software companies will begin to look more like hedge funds where models perform trades instead of people and agents write code instead of people.</p>

<h2 id="hypothesis">Hypothesis</h2>

<p>Rather than tackle <a href="https://programbench.com/tasks/">all the problems</a>, which may require a combination of techniques, we focused on the ones which were related to programming languages such as interpreters or compilers. LLMs are good at coding because code is just text, therefore shouldn’t a <a href="https://en.wikipedia.org/wiki/Programming_language_theory">PLT</a> toolkit help an agent across the finish line?</p>

<p>The original benchmark prohibits using the internet since an agent could cheat and clone the source code from GitHub. Unfortunately, this also prevents the agent from installing packages; nobody’s expected engineers to write software from scratch since the days of <a href="https://en.wikipedia.org/wiki/Bell_Labs">Bell Labs</a>! To preserve the original intent of not letting the agent find an easy solution off the web, this experiment sticks to one language and toolkit.</p>

<h2 id="approach">Approach</h2>

<p>We built a <a href="http://github.com/hdresearch/veldt">toolkit</a> with <a href="https://ocaml.org/">OCaml</a>, which already has an <a href="https://blog.darklang.com/compiling-dark-to-sql/">extensive history</a> of being <a href="https://comby.dev/">used for problems</a> related to <a href="https://ocaml.janestreet.com/ocaml-core/odoc/stdlib/Stdlib/Parsing/index.html">programming languages</a>. The toolkit was given to <a href="https://github.com/SWE-agent/mini-swe-agent">mini-swe-agent</a>, the same one used by Meta when publishing the original scores, as well as <a href="/blog/zagent">our harness</a> intended for greenfield development.</p>

<p>Interestingly, the former performed much better than the latter. A note on that is shared after the results table.</p>

<h2 id="results">Results</h2>

<p>Below shows a comparison of the progress made in the original publication using Sonnet 4.6 and mini-swe-agent. <a href="http://github.com/hdresearch/veldt"><code class="language-plaintext highlighter-rouge">veldt</code></a> is the name of the OCaml PLT toolkit and the <code class="language-plaintext highlighter-rouge">zagent captain</code> is what was used to make <a href="https://github.com/hdresearch/sterling/">sterling</a>, our open source OpenAPI-to-SDK generator.</p>

<p><strong>Note:</strong> Both <code class="language-plaintext highlighter-rouge">veldt</code> and <code class="language-plaintext highlighter-rouge">zagent</code> were run with Sonnet 4 (<code class="language-plaintext highlighter-rouge">claude-sonnet-4-20250514</code>) rather than 4.6</p>

<table>
  <thead>
    <tr>
      <th>Task</th>
      <th>Sonnet 4.6 (original)</th>
      <th>veldt</th>
      <th>zagent captain</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>jqlang/jq</td>
      <td>1.0%</td>
      <td>55.6%</td>
      <td>9.6%</td>
    </tr>
    <tr>
      <td>lua/lua</td>
      <td>34.7%</td>
      <td>46.7%</td>
      <td>12.1%</td>
    </tr>
    <tr>
      <td>luajit/luajit</td>
      <td>71.5%</td>
      <td>44.0%</td>
      <td>14.8%</td>
    </tr>
    <tr>
      <td>tree-sitter/tree-sitter</td>
      <td>37.2%</td>
      <td>35.9%</td>
      <td>26.5%</td>
    </tr>
    <tr>
      <td>paradigmxyz/solar</td>
      <td>42.9%</td>
      <td>33.7%</td>
      <td>24.9%</td>
    </tr>
    <tr>
      <td>parcel-bundler/lightningcss</td>
      <td>49.9%</td>
      <td>27.1%</td>
      <td>9.6%</td>
    </tr>
    <tr>
      <td>tinycc/tinycc</td>
      <td>9.3%</td>
      <td>4.9%</td>
      <td>3.4%</td>
    </tr>
    <tr>
      <td>typst/typst</td>
      <td>0.0%</td>
      <td>8.0%</td>
      <td>4.6%</td>
    </tr>
    <tr>
      <td>bellard/quickjs</td>
      <td>0.0%</td>
      <td>0.8%</td>
      <td>0.7%</td>
    </tr>
    <tr>
      <td>php/php-src</td>
      <td>0.0%</td>
      <td>0.6%</td>
      <td>2.3%</td>
    </tr>
    <tr>
      <td>Average</td>
      <td>24.5%</td>
      <td>25.7%</td>
      <td>10.8%</td>
    </tr>
  </tbody>
</table>

<p><strong>Note 2:</strong> Developing veldt was done with knowledge of the sorts of abstractions that would be useful in these tasks (as shown with <code class="language-plaintext highlighter-rouge">jq</code>/<code class="language-plaintext highlighter-rouge">lua</code> results being overfit due to there being more methods available for parsing). The validation is in other technically different problems showing benefits like <code class="language-plaintext highlighter-rouge">typst</code>.</p>

<p><code class="language-plaintext highlighter-rouge">zagent</code> failures stood out considering its intention for <a href="https://en.wikipedia.org/wiki/Greenfield_project">greenfield work</a>. My suspicion is it’s the same problem as internet data not containing reasoning data and only final outputs. Were an engineer to approach these problems from scratch (again, <a href="https://engineering.fb.com/2013/11/21/core-infra/under-the-hood-building-and-open-sourcing-rocksdb/">not a new problem</a>), they’d maybe start by deconstructing the requirements and rationales behind design decisions. As an analogy, <a href="https://www.youtube.com/watch?v=nTgeLEWr614">chimpanzees can memorize numbers</a> but we do not expect them to abstract away addition of numbers; coding agents can implement software but we’re not expecting them to explain the business requirements.</p>

<h2 id="conclusion">Conclusion</h2>

<p>We’re not yet at the point where software engineering from first principles is solved. Although, simply giving coding agents access to relevant tools makes it more productive, especially when they’re in the direction of abstractions and services related to the problem being solved. Otherwise, it’s comparable to making a web app in Assembly instead of Python, or sending SMS messages without Twilio, or using kubernetes in your stack without being able to explain why.</p>

<p>However, this does not mean agents can’t improve your productivity at a <a href="/blog/headless-browser-testing">small</a> or <a href="/blog/elixir-webassembly-billion-tokens">larger</a> scale. If you’re interested in having your own fleets of coding agents working for you, then we’ve got the platform for you. Go ahead over to our <a href="https://docs.vers.sh/overview">docs</a> to learn more and get started!</p>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[Prelude]]></summary></entry><entry><title type="html">Bringing Hermes to WebAssembly</title><link href="https://yev.bar/hermes-wasm" rel="alternate" type="text/html" title="Bringing Hermes to WebAssembly" /><published>2026-05-06T08:00:00+00:00</published><updated>2026-05-06T08:00:00+00:00</updated><id>https://yev.bar/hermes-wasm</id><content type="html" xml:base="https://yev.bar/hermes-wasm"><![CDATA[<h2 id="whats-in-this-post">What’s in this post?</h2>

<p>We took <a href="https://hermes-agent.nousresearch.com/">Hermes Agent</a>, developed by the folks at <a href="https://nousresearch.com/">Nous Research</a>, and brought it to WebAssembly in two different ways, detailed below, and share what our general takeaways from this experiment are.</p>

<h2 id="should-i-replace-my-hermes-with-one-of-these">Should I replace my Hermes with one of these?</h2>

<p>Probably not. If doing things with Python in WebAssembly is of interest to you, then continue reading!</p>

<h2 id="where-did-hermes-come-from">Where did Hermes come from?</h2>

<p>Having taken the world by storm, <a href="https://openclaw.ai">OpenClaw</a> is an AI assistant which uses the computer you run it on <a href="https://www.youtube.com/watch?v=WnzR5aOElvw">similarly to a person</a> (so it can do more than just <a href="https://youtu.be/7xTGNNLPyMI?si=5OR24vJniQglQL6j&amp;t=3121">recite Wikipedia articles</a>). Following this, Hermes was written in Python on top of <a href="https://github.com/SWE-agent/mini-swe-agent">mini-swe-agent</a> (unlike OpenClaw which is written in TypeScript on top of <a href="https://lucumr.pocoo.org/2026/1/31/pi/">pi</a>).</p>

<div class="mermaid">
graph LR
    subgraph outer[" "]
        direction LR
        subgraph inner["More tools + use computer"]
            A["Coding agent"]
        end
        inner --&gt;|"Same as"| B["General-purpose agent"]
    end
    style A fill:#1a1a2e,stroke:#58a6ff,stroke-width:2px,color:#c9d1d9
    style inner fill:#161b22,stroke:#58a6ff,stroke-width:1px,color:#8b949e
    style B fill:#238636,stroke:#2ea043,stroke-width:2px,color:#ffffff
    style outer fill:none,stroke:none
</div>

<p>Both are “general-purpose agents” which are really batteries-included coding agents. The “magic” of their utility comes from the assembling of the <a href="https://www.mendral.com/blog/agent-harness-belongs-outside-sandbox">“harness”</a> the agent sits inside of when handling users’ prompts; which gives it the capability to do things like click around a browser or send an email.</p>

<h2 id="why-webassembly">Why WebAssembly?</h2>

<p>I’m admittedly <a href="/blog/elixir-webassembly-billion-tokens">biased</a> when it comes to WebAssembly but, having worked on <a href="/blog/git-zig-bun-100x">multi-agent</a> <a href="/blog/zagent">projects</a> before, I was interested in seeing if WebAssembly would give a win with regard to isolation (ie spinning up multiple separate agents in parallel) or granular configurability (ie assembling agents precisely with certain tools for different “modes”).</p>

<p>Additionally, since Hermes is written in Python, I wanted to see if eagerly compiling to WebAssembly would offer closer-to-native performance.</p>

<h2 id="how-to-wasm-hermes">How to WASM Hermes</h2>

<h3 id="pyodide">Pyodide</h3>

<h4 id="run-hermes-in-pyodide-yourself">Run Hermes in pyodide yourself</h4>

<p>This app serves an <code class="language-plaintext highlighter-rouge">index.html</code> with the full agent running client-side in Pyodide. To run the below command, you may need to <a href="https://docs.vers.sh/installation">install the <code class="language-plaintext highlighter-rouge">vers</code> CLI</a> first.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vers run-commit f46a2b21-73fe-4835-ad79-8eccd523fc07 <span class="se">\</span>
  <span class="nt">--format</span> json <span class="nt">--wait</span> <span class="se">\</span>
  | <span class="nb">sed</span> <span class="nt">-n</span> <span class="s1">'s/.*"vm_id"[: ]*"\([^"]*\)".*/https:\/\/\1.vm.vers.sh/p'</span>
</code></pre></div></div>

<p><strong>Public Vers VM Commit:</strong> <code class="language-plaintext highlighter-rouge">f46a2b21-73fe-4835-ad79-8eccd523fc07</code></p>

<h4 id="how-it-works-with-pyodide">How it works with Pyodide</h4>

<div class="mermaid">
graph LR
    subgraph outer[" "]
        direction LR
        subgraph pyodide["Pyodide"]
            A["Hermes agent"]
        end
        subgraph browser["Web browser"]
            W["WebAssembly"]
        end
        pyodide --&gt; W
    end
    style A fill:#1a1a2e,stroke:#58a6ff,stroke-width:2px,color:#c9d1d9
    style pyodide fill:#161b22,stroke:#58a6ff,stroke-width:1px,color:#8b949e
    style W fill:#1a1a2e,stroke:#f0883e,stroke-width:2px,color:#c9d1d9
    style browser fill:#161b22,stroke:#f0883e,stroke-width:1px,color:#8b949e
    style outer fill:none,stroke:none
</div>

<p>The first approach is by using <a href="https://pyodide.org/en/stable/">Pyodide</a>, a Python runtime that’s ported to WebAssembly so Python programs can be interpreted and run in the browser. You can think of this as being similar to the approach that was taken with <a href="https://supabase.com/blog/postgres-wasm">bringing Postgres to WebAssembly</a>:</p>

<div class="mermaid">
graph LR
    subgraph outer[" "]
        direction LR
        subgraph buildroot["Linux VM created with Buildroot"]
            P["Postgres"]
        end
        subgraph browser2["Web browser"]
            W2["WebAssembly"]
        end
        buildroot --&gt; W2
    end
    style P fill:#1a1a2e,stroke:#58a6ff,stroke-width:2px,color:#c9d1d9
    style buildroot fill:#161b22,stroke:#58a6ff,stroke-width:1px,color:#8b949e
    style W2 fill:#1a1a2e,stroke:#f0883e,stroke-width:2px,color:#c9d1d9
    style browser2 fill:#161b22,stroke:#f0883e,stroke-width:1px,color:#8b949e
    style outer fill:none,stroke:none
</div>

<p>Postgres itself doesn’t get run in WebAssembly but instead a Linux emulator in WASM runs a modified version of Postgres so the whole thing can actually work together inside a browser.</p>

<h4 id="hermes-in-pyodide-source">Hermes in Pyodide source</h4>

<p>You can view and modify the source code here: https://github.com/hdresearch/hermes-pyodide</p>

<h3 id="pywasm">pywasm</h3>

<h4 id="run-hermes-in-pywasm-yourself">Run Hermes in pywasm yourself</h4>

<p>This app serves the <code class="language-plaintext highlighter-rouge">hermes_agent.wasm</code> binary. Hit the “Run” button in the UI to execute it live.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vers run-commit f83df1ac-0a53-4ca6-bc26-205584fe65a3 <span class="se">\</span>
  <span class="nt">--format</span> json <span class="nt">--wait</span> <span class="se">\</span>
  | <span class="nb">sed</span> <span class="nt">-n</span> <span class="s1">'s/.*"vm_id"[: ]*"\([^"]*\)".*/https:\/\/\1.vm.vers.sh/p'</span>
</code></pre></div></div>

<p><strong>Public Vers VM Commit:</strong> <code class="language-plaintext highlighter-rouge">f83df1ac-0a53-4ca6-bc26-205584fe65a3</code></p>

<h4 id="how-it-works-with-py2wasm">How it works with py2wasm</h4>

<p>This second approach works by using <a href="https://wasmer.io/posts/py2wasm-a-python-to-wasm-compiler">py2wasm</a>, a Python-to-WebAssembly compiler and the pywasm split design keeps the security boundary clean:</p>

<div class="mermaid">
graph LR
    A["Hermes"] --&gt; B

    subgraph B["WASM"]
        B1["• Prompt<br />• Loop<br />• Context<br />• Local tools"]
    end

    B --&gt;|"JSON in/out"| C

    subgraph C["Host"]
        C1["• Calling API<br />• Tool dispatch<br />• API keys"]
    end
</div>

<p>The host extracts real schemas from the Hermes’ <code class="language-plaintext highlighter-rouge">ToolRegistry</code> at startup before sending them to the WASM binary via init protocol. The LLM always sees the same parameter names as the actual handlers (ie <code class="language-plaintext highlighter-rouge">path</code> instead of <code class="language-plaintext highlighter-rouge">file_path</code> or <code class="language-plaintext highlighter-rouge">old_string</code> instead <code class="language-plaintext highlighter-rouge">old_text</code>).</p>

<h4 id="hermes-in-pywasm-source">Hermes in pywasm source</h4>

<p>You can view and modify the soure code yourself here: https://github.com/hdresearch/hermes-pywasm</p>

<h2 id="benchmarks">Benchmarks</h2>

<p>Below are benchmarks obtained from running on a M4 macbook. As <a href="#should-i-replace-my-hermes-with-one-of-these">admitted earlier</a>, this probably won’t meaningfully replace running Hermes on your laptop. However, if porting the harness itself to alternative environments (ie a browser) is of interest to you, then you can see some of the tradeoffs between <a href="#pyodide">pyodide</a> and <a href="#pywasm">py2wasm</a>.</p>

<table>
  <thead>
    <tr>
      <th>Metric</th>
      <th>Native Python</th>
      <th>Pyodide (browser)</th>
      <th>pywasm (WASI)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Cold start</td>
      <td>750 ms</td>
      <td>2–5 s</td>
      <td><strong>110 ms</strong></td>
    </tr>
    <tr>
      <td>Single turn</td>
      <td>840 ms</td>
      <td>~850 ms</td>
      <td><strong>100 ms</strong></td>
    </tr>
    <tr>
      <td>20-turn conversation</td>
      <td>3,280 ms</td>
      <td>~3,300 ms</td>
      <td><strong>110 ms</strong></td>
    </tr>
    <tr>
      <td>50 parallel agents</td>
      <td>4,566 ms</td>
      <td>N/A (browser)</td>
      <td><strong>611 ms</strong> (wasmtime)</td>
    </tr>
    <tr>
      <td>Worker pool throughput</td>
      <td>9 q/s</td>
      <td>N/A (browser)</td>
      <td><strong>81 q/s</strong> (wasmtime)</td>
    </tr>
    <tr>
      <td>Deployment size</td>
      <td>733 MB</td>
      <td>~20 MB + packages</td>
      <td><strong>26 MB</strong></td>
    </tr>
    <tr>
      <td>Pip packages</td>
      <td>171</td>
      <td>171 (via Pyodide)</td>
      <td>0</td>
    </tr>
    <tr>
      <td>Runs in browser</td>
      <td>❌</td>
      <td>✅</td>
      <td>⚠️ needs WASI polyfill</td>
    </tr>
    <tr>
      <td>API key exposure</td>
      <td>server-side</td>
      <td>client-side</td>
      <td>Stored in host</td>
    </tr>
  </tbody>
</table>

<h2 id="takeaways">Takeaways</h2>

<p>While the founder of Docker years ago suggested <a href="https://x.com/solomonstre/status/1111004913222324225?lang=en">WASM+WASI was the missing sandboxing solution</a>, it’s evidently not a magic bullet considering the missing capabilities from a full-fledged computer or container. If having a full but branchable VM with incredibly fast startup times sounds like what you’re looking for, then go on over to <a href="https://vers.sh">Vers</a> and get started!</p>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[What’s in this post?]]></summary></entry><entry><title type="html">Taking MemPalace to 100%</title><link href="https://yev.bar/retaining" rel="alternate" type="text/html" title="Taking MemPalace to 100%" /><published>2026-05-06T08:00:00+00:00</published><updated>2026-05-06T08:00:00+00:00</updated><id>https://yev.bar/retaining</id><content type="html" xml:base="https://yev.bar/retaining"><![CDATA[<h2 id="overview">Overview</h2>

<p>We took <a href="https://github.com/mempalace/mempalace">MemPalace</a> and extended its techniques to close the gap in the <a href="https://github.com/mempalace/mempalace#benchmarks">LongMemEval</a> <code class="language-plaintext highlighter-rouge">recall@5</code> retrieval benchmark to get a reproducible 100% score using only local compute (no LLM or API calls).</p>

<h2 id="what-this-is-not">What this is not</h2>

<ul>
  <li><strong>Not a LongMemEval leaderboard score.</strong> The full LongMemEval benchmark is end-to-end and involves generating answers plus GPT-4 judging. This experiment is strictly about the same retrieval metric that MemPalace was tackling.</li>
  <li><strong>Not a strong metric.</strong> The metric is <code class="language-plaintext highlighter-rouge">recall_any@5</code>, the softer variant. <code class="language-plaintext highlighter-rouge">recall_all@5</code> (requiring <em>every</em> gold session in the top 5) would be a harder bar.</li>
  <li><strong>Not an novel algorithm.</strong> Iterating on failures from the dataset, the patches made are general NLP patterns. A new benchmark could be put together with different heuristics required but that just continues the cat-and-mouse game of developing “human-comparable intelligence”.</li>
</ul>

<p>These caveats aren’t intended to steer your attention away but more set the expectation of an interesting result. The central takeaway of grammatical patterns in text being applicable to vector stores still deserves some acknowledgement.</p>

<h2 id="what-we-did-do">What we did do</h2>

<p>We achieved 100% <code class="language-plaintext highlighter-rouge">recall@5</code> retrieval on all 500 LongMemEval questions. The system uses no language model, makes no API calls, and requires no GPU. The MemPalace baseline on the same metric is 96.6%, so the +3.4% improvement represents a real engineering output. Shared in a project dubbed <a href="https://github.com/hdresearch/retaining">Retaining</a>, it does:</p>

<ul>
  <li><strong>500/500 R@5</strong> (100% recall at rank 5)</li>
  <li><strong>500/500 R@10</strong> (100% recall at rank 10)</li>
  <li>Fully deterministic and reproduced across multiple runs</li>
</ul>

<h2 id="context">Context</h2>

<p>On April 6th, <a href="https://x.com/bensig/status/2041229266432733356">Ben Sigman shared</a> that Milla Jovovich had fun with coding agents and built a solution for long-term memory named “MemPalace”. For those who are fans of sci-fi movies, you may recognize Jovovich as the one who played the <a href="https://en.wikipedia.org/wiki/Milla_Jovovich#Breakthrough_(1997%E2%80%932001)">Fifth Element</a> as well as Alice in <a href="https://en.wikipedia.org/wiki/Resident_Evil_(film_series)">Resident Evil</a>. The cherry on top is, at the <a href="https://en.wikipedia.org/wiki/Resident_Evil:_The_Final_Chapter#Plot">end of the Resident Evil series</a>, Alice is enabled to tackle the antagonist after her childhood memories were uploaded to her; a rather similar message to enabling agents after giving them a “memory palace”.</p>

<p>Originally proclaiming it to score <a href="https://github.com/MemPalace/mempalace/commit/068dbd9a7be0af3c37bbbf1ed0e3dc477f850af8">100% with optional Haiku rerank</a> before backtracking, it’s racked up a good volume of attention and validation so it’s not totally “viral slop”. By both <a href="https://mempalaceofficial.com/#dialect">compressing content</a> and <a href="https://mempalaceofficial.com/concepts/the-palace.html">making historical context navigatable</a>, it highlights the efficacy of simple NLP techniques when applied with LLMs.</p>

<h2 id="improving-mempalace">Improving MemPalace</h2>

<h3 id="what-worked">What worked</h3>

<p>If you’ve seen structured note taking like the <a href="https://lsc.cornell.edu/how-to-study/taking-notes/cornell-note-taking-system/">Cornell Note Taking System</a> or <a href="https://obsidian.md/help/plugins/backlinks">backlinks in Obsidian</a>, then you know there’s more to outlining text than just indexing when or where words occur. With <a href="https://spacy.io/">spaCy</a> and <a href="https://spacy.io/usage/linguistic-features#named-entities">named entity recognition</a>, we can extend the existing pipeline by including noun phrases or other grammatical relations that give a more detailed picture of the “ontology” representing the content at hand.</p>

<p>Below is a table of newly added techniques and how much they contributed to the <code class="language-plaintext highlighter-rouge">recall@5</code> performance:</p>

<table>
  <thead>
    <tr>
      <th>Technique</th>
      <th>Measurement</th>
      <th>Net Δ R@5</th>
      <th>Net Qs Fixed</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>NER-enriched synthetic documents</td>
      <td>individual</td>
      <td>+1.6%</td>
      <td>+8</td>
    </tr>
    <tr>
      <td>Keyword overlap re-ranking</td>
      <td>individual</td>
      <td>+1.2%</td>
      <td>+6</td>
    </tr>
    <tr>
      <td>Time-based date matching</td>
      <td>individual</td>
      <td>+0.8%</td>
      <td>+4</td>
    </tr>
    <tr>
      <td>Logic engine scores</td>
      <td>individual</td>
      <td>+0.4%</td>
      <td>+2</td>
    </tr>
    <tr>
      <td>Theme detection</td>
      <td>individual</td>
      <td>+0.2%</td>
      <td>+1</td>
    </tr>
    <tr>
      <td>NP embeddings + LogicKB rewrite</td>
      <td>cumulative</td>
      <td>+0.4%</td>
      <td>+2</td>
    </tr>
    <tr>
      <td>Rank preservation injection</td>
      <td>cumulative</td>
      <td>+0.6%</td>
      <td>+3</td>
    </tr>
    <tr>
      <td>Temporal-NP bridge</td>
      <td>cumulative</td>
      <td>+0.2%</td>
      <td>+1</td>
    </tr>
  </tbody>
</table>

<p><em>Individual: technique alone added to the baseline. Cumulative: technique added on top of prior ones. Deltas overlap and do not sum to total.</em></p>

<p>The top three contributors are all simple re-ranking heuristics. The logic engine contributes modestly and actually causes the most regressions. The finding from this experiment: <strong>enrichening NLP extraction in a retrieval pipeline can produce more than improving the logic engine that queries them.</strong> (<a href="https://www.cs.utexas.edu/~eunsol/courses/data/bitter_lesson.pdf">damn you bitter lesson!</a>)</p>

<h3 id="in-more-detail">In more detail</h3>

<h4 id="1-spacy-based-extraction">1. spaCy-based extraction</h4>

<p>Every session gets processed through spaCy’s <code class="language-plaintext highlighter-rouge">en_core_web_sm</code> pipeline. We extract entities, noun phrases, relations (subject-verb-object triples), time-related markers, and quoted phrases. This takes ~5 seconds per question’s haystack when run on my Macbook.</p>

<h4 id="2-pure-python-logic-engine">2. Pure-Python logic engine</h4>

<p>A <code class="language-plaintext highlighter-rouge">LogicKB</code> Python class that stores extracted facts as inverted indexes. For each query, it looks up matching objects across all sessions, returning a weighted score per each one. This replaced an earlier Prolog approach with the same idea but much less complexity and no IPC overhead.</p>

<h4 id="3-ner-enriched-synthetic-documents">3. NER-enriched synthetic documents</h4>

<p>For each session, we create an document containing its extracted facts and details. These get indexed alongside the raw session text, giving the embedding model a richer retrieval surface. This is the single biggest contributor to accuracy.</p>

<h4 id="4-noun-phrase-embedding-bridge">4. Noun-phrase embedding bridge</h4>

<p>We embed each session’s extracted objects into a separate ChromaDB collection and query it with the question’s noun phrases. This bridges gaps that neither keywords nor full-document embeddings can cross. “Battery life phone” → “portable power bank” has a close enough embedding distance in the noun phrase space to pick up the right session.</p>

<h4 id="5-time-related-bridge">5. Time related bridge</h4>

<p>For time-related questions (“What did I buy 10 days ago?”), we first identify all sessions in the date window, then run the noun phrase bridge <em>within that filtered set</em>. This discriminates between 14 sessions that all share the same date by finding the one whose noun phrases are topically closest to the question.</p>

<h3 id="what-didnt-work">What didn’t work</h3>

<p>When people complain about LLMs not being able to answer questions or hallucinating false information, what nobody complains about is the LLMs’ ability to identify the question it needs to answer (we can depend on AI to write code that does a thing rather than depend on it to end-to-end handle a task). In the subject of answering questions or digging through “long context problems”, I first attempted to have the LLM use <a href="https://en.wikipedia.org/wiki/Prolog">Prolog</a> for storing and retrieving facts.</p>

<p>However, the semantic fuzziness (ie synonyms or finding similar topics to a query) ended up hurting the overall score more than helping. The approach in MemPalace to depend on a <a href="https://www.trychroma.com">vector store</a> actually showed to be “more correct” in this experiment.</p>

<p>Nevertheless, I do think there may be types of problems where realistic input queries (ignoring cases where people are funny and test jailbreaking support agents) would be usable with a more structured and queryable store of relations between objects. Prolog just may not be a low-hanging fruit solution for long-term memory problems where semantic similarity is something worth indexing.</p>

<h2 id="running-yourself">Running yourself</h2>

<p>First, clone the repo and install dependencies.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/hdresearch/retaining
<span class="nb">cd </span>retaining
python3 <span class="nt">-m</span> venv .venv <span class="o">&amp;&amp;</span> <span class="nb">source</span> .venv/bin/activate
pip <span class="nb">install </span>spacy chromadb
python <span class="nt">-m</span> spacy download en_core_web_sm
</code></pre></div></div>

<p>Next, download the dataset for the benchmark.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Download LongMemEval data (~265MB)</span>
curl <span class="nt">-fsSL</span> <span class="nt">-o</span> /tmp/longmemeval_s_cleaned.json <span class="se">\</span>
  https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_s_cleaned.json
</code></pre></div></div>

<p>Lastly, run the benchmarks.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Vector-only baseline: 96.6% R@5, ~5 min</span>
python bench_v2.py /tmp/longmemeval_s_cleaned.json <span class="nt">--mode</span> vector

<span class="c"># Full hybrid: 100% R@5, ~50 min</span>
python bench_v2.py /tmp/longmemeval_s_cleaned.json <span class="nt">--mode</span> hybrid
</code></pre></div></div>

<p>No API keys. No GPU. Python 3.9+ and ~300MB of disk.</p>

<h2 id="conclusion">Conclusion</h2>

<p>AI famously hit <a href="https://en.wikipedia.org/wiki/AI_winter">“winters”</a> in the past when some wall prevented computers from becoming sufficiently intelligent. Interestingly, the problem in the past was that “symbolic” approaches to AI would fall short when it came to <a href="https://data-mining.philippe-fournier-viger.com/the-semantic-web-and-why-it-failed/">the last mile of complexity</a>. Similarly, LLM-maximalist approaches also run into a “last mile problem” when it comes to ensuring accuracy of details (ie hallucination).</p>

<p>By incorporating older NLP techniques to tackle the “last mile problems” with modern approaches involving LLMs, there are rather interesting results to be found! Albeit, the implementation used here to game <code class="language-plaintext highlighter-rouge">recall@5</code> is, certainly, by no means a complete solution for knowledge retrieval.</p>

<p>The beauty of the finding is that the problem of “if only someone had sat down long enough to write every NLP grammar rule” now becomes somewhat negligible in a world with coding agents. So, rather than continue to see human text as black boxes, know that a richer pipeline may get the sufficient amount of complexity for some information to be adequately indexed.</p>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[Overview]]></summary></entry><entry><title type="html">A coding agent with direction</title><link href="https://yev.bar/zagent" rel="alternate" type="text/html" title="A coding agent with direction" /><published>2026-04-29T08:00:00+00:00</published><updated>2026-04-29T08:00:00+00:00</updated><id>https://yev.bar/zagent</id><content type="html" xml:base="https://yev.bar/zagent"><![CDATA[<h2 id="contents">Contents</h2>

<ul>
  <li><a href="#what-is-this">What is this?</a></li>
  <li><a href="#what-is-this-not">What is this not?</a></li>
  <li><a href="#the-background">The background</a>
    <ul>
      <li><a href="#coding-agent">Coding agent</a></li>
      <li><a href="#ralph-loops">Ralph loops</a></li>
      <li><a href="#rlms">RLMs</a></li>
    </ul>
  </li>
  <li><a href="#zagent-terms">zagent terms</a>
    <ul>
      <li><a href="#code-cannon">Code cannon</a></li>
      <li><a href="#code-pirate">Code pirate</a></li>
      <li><a href="#code-captain">Code captain</a></li>
    </ul>
  </li>
  <li><a href="#takeaways">Takeaways</a></li>
</ul>

<h2 id="what-is-this">What is this?</h2>

<p>This is an overview of the principles I used to assemble <a href="https://github.com/hdresearch/zagent">zagent</a>, a coding harness for getting more progress out of a single “shot”. I’ll be both describing the topics I worked on top of as well as the structure behind what I put together.</p>

<p>If you’re looking for a post to read that gives copy-and-paste’able commands, this isn’t for you. If you’re alright with reading something more explanatory, then continue on!</p>

<h2 id="what-is-this-not">What is this not?</h2>

<p>zagent is not going to replace your Claude code or <code class="language-plaintext highlighter-rouge">pi</code> (which I predominately use) but the ideas below should be high level enough you can implement it in your own harnesses or coding agent systems.</p>

<h2 id="the-background">The background</h2>

<p>I didn’t by any means invent a new model or algorithm, I simply applied together some existing concepts which are accessible yourself. Being transparent, this is my way of building up towards a general purpose version of what Google accomplished with <a href="https://deepmind.google/blog/alphaevolve-a-gemini-powered-coding-agent-for-designing-advanced-algorithms/">AlphaEvolve</a>.</p>

<p>To break down what the heck is going on with <code class="language-plaintext highlighter-rouge">zagent</code>, there are three “primitives” in the area of coding agents that would be useful to know.</p>

<h3 id="coding-agent">Coding agent</h3>

<p>From editors like <a href="https://cursor.com">Cursor</a> to headless systems like <a href="https://devin.ai">Devin</a>, there’s a large variety of offerings that all fall under the notion of “coding agents”. Simplifying it to the bare minimum, a coding agent is an AI that can take a prompt from someone and write code to accomplish some goal. However it may be accessed by a user (could be tagging in a Slack workspace, sending a message on Telegram, writing a prompt from a UI, etc), the underlying step from general agents is that it can write and run code.</p>

<div class="mermaid">
graph LR;
    Prompt--&gt;Agent
    Agent["Coding agent\n(Can write/run code)"]--&gt;Output
</div>

<p>Sometimes underappreciated in domains other than literally writing software, the power of coding agents is in how much is built on top of code, making them immediately ‘effective’ in the world around us today. It could be a “short lived” agent that only runs to solve a specific problem before exiting or a “long running” agent with a growing memory. Depending on the particular use case you’re looking for, one may be better than another.</p>

<p>In the context of writing software that <em>delivers something</em>, I’ve personally found the philosophy of short lived agents to be better suited.</p>

<h3 id="ralph-loops">Ralph loops</h3>

<p><a href="https://awesomeclaude.ai/ralph-wiggum">Ralph Wiggum loops</a>, named literally after the <a href="https://en.wikipedia.org/wiki/Ralph_Wiggum">Simpsons character</a>, is a technique for working with coding agents that looks something like the below pseudo-code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>while not done:
    fire coding agent at task(s)
    repeat until done
</code></pre></div></div>

<p>For instance, the Claude code plugin would run a <code class="language-plaintext highlighter-rouge">while true</code> loop in bash until the LLM outputted a specific string indicating it had actually completed the task rather than said things which sounded nice. In pseudo-code that’d look something like:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="s">"DONE"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">last_output</span><span class="p">:</span>
    <span class="n">fire</span> <span class="n">claude</span> <span class="n">code</span> <span class="ow">and</span> <span class="n">tell</span> <span class="n">it</span> <span class="n">to</span> <span class="n">say</span> <span class="s">"DONE"</span> <span class="n">when</span> <span class="n">finished</span>
    <span class="k">continue</span> <span class="n">until</span> <span class="n">done</span>
</code></pre></div></div>

<p>To avoid running out of context (and to keep productive when one goes to sleep), folks would run “ralph loops” since the <code class="language-plaintext highlighter-rouge">while true</code> serves as a way to reset the context over and over, letting it run ‘infinitely’. In the case of problems where the task is going through a large bullet point list of items (ie meticulously writing unit tests across a large codebase), it works well since the tokens that filled up the context about prior solved items isn’t relevant to the context needed for solving problems moving forward.</p>

<div class="mermaid">
graph LR;
    Prompt["Send prompt before going to sleep"]--&gt;Ralph
    Ralph["Ralph loop\n(Resetting and repeating over and over till it's done)"]--&gt;Goal["Completed goal"]
    Ralph--&gt;Ralph
</div>

<p>However, in the case of problems where you do lose something by resetting the context (ie a complex integration which requires knowing about all the pieces involved to be useful), then ralph loops can fall short. While still a useful technique, it’s no longer meme’d as a solution for “solving programming” for this reason.</p>

<h3 id="rlms">RLMs</h3>

<p>An idea popularized from <a href="https://alexzhang13.github.io/blog/2025/rlm/">a blog post</a> and then published to <a href="https://arxiv.org/abs/2512.24601">arXiv</a>, RLMs broadly solve the problem of “running out of context” but in an importantly different way. Rather than place the “infinite loop” above the LLM (like done in the ralph loop), what if the loop were conceptually brought into the agent loop itself? In RLMs, this is done by letting the agent recursively call itself or other agents before coming back with a final answer.</p>

<div class="mermaid">
graph LR;
    Prompt--&gt;Agent
    Agent--&gt;Sub["Sub-agent"]
    Sub--&gt;Web["Web request"]
    Sub--&gt;Code["Run code"]
    Sub--&gt;Process["Process results"]
    Sub--&gt;Agent
    Agent--&gt;Result
</div>

<p>Explaining how this works with LLMs but with an analogy: suppose you wake up to a text message asking you to research something that you have five minutes to respond to but you haven’t had the chance to even have coffee yet. Lacking the energy to Google around, you text someone else who you think either knows the answer already or wouldn’t mind finding it, they get back with the answer, you forward to the first person, and then all’s done.</p>

<p>A profound utility from this is being able to “stretch” your context window since spawned sub-agents can go through their context windows exploring something rather than the top-level agent you provided the original prompt to. Nowadays, in conjunction with stuff like <a href="https://github.com/mempalace/mempalace">memory</a>, some of the older problems with arbitrarily large context windows have tools for tackling them.</p>

<p>Where “infinite context” can fall short can be broadly explained by how <a href="https://www.youtube.com/watch?v=G_7Ta_4coy4">“completely illuminating a house such there no shadows”</a> makes it uninhabitable. It’s no secret LLMs can be convincing whether to themselves to users falling into AI psychosis. As a result, letting an agent ruminate on some goal or task (even if it’s rational like programming), can lead to adverse results which are seen as unproductive to the person hoping to finish an app or such.</p>

<h2 id="zagent-terms">zagent terms</h2>

<p>Inspired by my experience with <a href="https://x.com/training_loop/status/2024600194428424668">herding</a> coding agents, there are three layers I’ve assembled into <code class="language-plaintext highlighter-rouge">zagent</code> that apply the above ideas. Before you ask, yes, the names are inspired by <a href="https://en.wikipedia.org/wiki/One_Piece">One Piece</a>.</p>

<h3 id="code-cannon">Code cannon</h3>

<p>In my prior projects using “code cannons” like <a href="https://vers.sh/blog/git-zig-bun-100x">rewriting git in zig</a> or <a href="https://vers.sh/blog/elixir-webassembly-billion-tokens">developing a modern toolkit between Elixir and WebAssembly</a>, what I was really doing was leveraging <a href="https://vers.sh">Vers</a> VMs as the RLM environments in which sub-agents were working on scoped problems. To differentiate from the ideal of a <a href="https://github.com/gastownhall/gastown">code factory</a>, this RLM pattern is what I’ve referred to as a “code cannon”.</p>

<div class="mermaid">
graph TD;
    Agent--&gt;Sub1["Sub-agent"]
    Agent--&gt;Sub2["Sub-agent"]
    Agent--&gt;Sub3["Sub-agent"]

    subgraph cannon[" "]
        Sub1
        Sub2
        Sub3
        Sub1--&gt;RF1["Read file"]
        Sub1--&gt;WF1["Write file"]
        Sub1--&gt;RP1["Run program"]
        Sub2--&gt;RF2["Read file"]
        Sub2--&gt;WF2["Write file"]
        Sub2--&gt;RP2["Run program"]
        Sub3--&gt;RF3["Read file"]
        Sub3--&gt;WF3["Write file"]
        Sub3--&gt;RP3["Run program"]
    end
</div>

<p>In the case of rewriting the <code class="language-plaintext highlighter-rouge">git</code> CLI, there are several subcommands which can be worked on in parallel (and on different files which can prevent conflicts when merging changes). You can think of this like how, at a hackathon, you may have one person working on the backend, one person working on the frontend, and one person working on the slideshow presentation; each of them can work on their piece of the overall project without stepping on each others’ toes.</p>

<h3 id="code-pirate">Code pirate</h3>

<p>Taking a step back and contemplating what I was really doing when “firing code cannons”: I would see what the progress or status of changes were, break down the next wave of changes I wanted to see, provisioning new agents with their respective prompts, and letting it run for a while before coming back to my laptop and repeating.</p>

<p>Enter the “code pirate”, a ralph loop that works from a markdown file firing code cannons until it finishes more substantial progress.</p>

<div class="mermaid">
graph LR;
    Pirate["Code pirate"]--&gt;Pirate
    Pirate--&gt;SA1
    Pirate--&gt;SA2
    Pirate--&gt;SA3
    Pirate--&gt;SA4

    subgraph pair1["Code cannon"]
        SA1["Sub-agent"]
        SA2["Sub-agent"]
    end

    subgraph pair2["Code cannon"]
        SA3["Sub-agent"]
        SA4["Sub-agent"]
    end
</div>

<p>By bridging together the context-resetting of the Ralph loop (the pirate) and the context-mindfulness of the RLMs (the cannons), it establishes a coding harness which is able to accomplish larger diffs like building out <a href="https://github.com/hdresearch/sterling">sterling</a> (if it’s still private, it’s coming soon!).</p>

<p>When I come back to my computer to review a captain result, it’s less about knitting knots in feature intentions and more about steering the army of coding agents overall. Making sterling with the code pirate was less about firing it over and over at a goal but more setting goal(s), it finishes them through, and then setting new goals to be implemented (like <a href="https://vers.sh/blog/git-zig-bun-100x#why-we-think-this-works">making a peanut butter jelly sandwich</a>).</p>

<h3 id="code-captain">Code captain</h3>

<p>Everything up to this point I can say truthfully has yielded a real result that would have taken more time or effort if I used a different tool. This next “layer” is something I’ve been tinkering with and have not yet found something that feels like I “cracked it”. However, I’m sharing here in case the concepts are of use to someone else facing similar problems.</p>

<p>When tackling projects where it “working” is non-negotiable (ie it meets a test coverage quota, an ambiguity that would lead some agents to giving up early), totally depending on the LLM to come back with a result can be anticlimatic.</p>

<p>To remedy this while tinkering with <a href="https://lean-lang.org/">Lean</a>, I’ve started working on a “code captain” which behaves like a code pirate but, rather than let the agent exit when it’s gone astray, I added a gate which prevents the pirate from exiting until <em>all</em> conditions are met.</p>

<div class="mermaid">
graph LR;
    subgraph pirate["Repeat until complete"]
        Pirate["Code captain"]--&gt;Pirate
    end
    pirate--&gt;SA1
    pirate--&gt;SA2
    pirate--&gt;SA3
    pirate--&gt;SA4

    subgraph pair1["Code cannon"]
        SA1["Sub-agent"]
        SA2["Sub-agent"]
    end

    subgraph pair2["Code cannon"]
        SA3["Sub-agent"]
        SA4["Sub-agent"]
    end
</div>

<p>If the gate’s not well defined, then the agent can find a way to exit early. If the gate’s redefinable (ie learning about new objectives or constraints over time) or even appendable, then the agent may still find a way to exit early. So, ultimately, software engineering’s a game of scoping objectives well.</p>

<h2 id="takeaways">Takeaways</h2>

<p>Training employees versus hiring interns is like the difference between vertical and horizontal scaling. Likewise, the difference between leveling up a single person versus spinning up agents to fill in certain tasks is like the difference between vertical and horizontal scaling but for responsibilities. The underlying problem with coding harnesses is boiling down the responsibilities of a software engineer into horizontally scalable skills.</p>

<p>It’s already the case in some hedge funds that folks will develop models for executing strategies but aren’t picking up the phone and placing orders themselves. While there are still some firms which rely on old fashioned methods, the analog to software is that there will eventually be categories of products where the code defining these products isn’t governed by people but instead by the systems established by them.</p>

<p>Until the day coding’s finally solved, we shall still have problems to solve. Hack the planet!</p>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[Contents]]></summary></entry><entry><title type="html">Ziggit</title><link href="https://yev.bar/ziggit" rel="alternate" type="text/html" title="Ziggit" /><published>2026-04-02T08:00:00+00:00</published><updated>2026-04-02T08:00:00+00:00</updated><id>https://yev.bar/ziggit</id><content type="html" xml:base="https://yev.bar/ziggit"><![CDATA[<h2 id="digest">Digest</h2>

<p>We rewrote git in zig and:</p>

<ul>
  <li>Sped up bun by <a href="#bun-improvements">100x</a></li>
  <li>Got <a href="#git-drop-in">4x</a> faster than <code class="language-plaintext highlighter-rouge">git</code> on an arm Macbook</li>
  <li>Compiled to WASM to be <a href="#webassembly">5x smaller with 8.5x more exports</a>
    <ul>
      <li>Check out <a href="https://vers.sh/ziggit-demo">this demo to clone a repo</a> in your browser!</li>
    </ul>
  </li>
</ul>

<p>Rather than start with the theory behind the “swarming”, we’ll share how to code cannon yourself, describe how our zig rewrite of git went, and then dive into some of our theory behind why this works.</p>

<h2 id="how-to-code-cannon-yourself">How to code cannon yourself</h2>

<h3 id="install-vers-cli">Install vers CLI</h3>

<p>First you’ll need the <a href="https://github.com/hdresearch/vers-cli">vers CLI</a> installed.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/hdresearch/vers-cli/main/install.sh | sh
</code></pre></div></div>

<p>After you’ve installed it, log in.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vers login
</code></pre></div></div>

<p>Now you have a working <code class="language-plaintext highlighter-rouge">vers</code> CLI ready to prepare your swarm infrastructure.</p>

<h3 id="configure-environment-variables">Configure environment variables</h3>

<p>With the <code class="language-plaintext highlighter-rouge">vers</code> CLI you can define environment variables which get injected to all the VMs you create, making authentication for some CLIs a breeze. Here we’ll walk through the environment variables we included for this project.</p>

<p>First, create a <a href="https://github.com/new">new GitHub repository</a> and then follow <a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token">the instructions for creating a fine-grained personal access token</a>. You’ll want to create one that has <strong>Read and write</strong> access to content for the repository you’re going to work on.</p>

<p><img src="https://vers.sh/hdr_legacy/images/github-token.png" alt="github-token" /></p>

<p>We configured it to have access to <em>only</em> the repos we’re interested in code cannoning at for this project. Our rationale being we don’t want one or multiple agents to get creative and start integrating other projects that aren’t relevant. Once you have that API key then set it like so:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vers <span class="nb">env set </span>GITHUB_API_KEY github_pat_...
</code></pre></div></div>

<p>Next, from the <a href="https://vers.sh/orgs/yev/dashboard">vers dashboard</a> click on the <strong>API Keys</strong> tab and create a new API key. After you’ve written it down someplace you won’t lose it, you can set it to your environment variables (so an agent running in a VM would be able to on its own spawn further agents).</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vers <span class="nb">env set </span>VERS_API_KEY abc123...
</code></pre></div></div>

<p>Finally, since we’ve been driving this using <a href="https://claude.ai">Claude</a>, let’s set an <code class="language-plaintext highlighter-rouge">ANTHROPIC_API_KEY</code> so any coding agent running in a VM works out of the box.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vers <span class="nb">env set </span>ANTHROPIC_API_KEY sk-ant-...
</code></pre></div></div>

<h3 id="write-your-initial-plan">Write your initial plan</h3>

<p>We’ve shared the <a href="#the-initial-plan"><code class="language-plaintext highlighter-rouge">plan.md</code> file</a> we used for the zig rewrite of git, you’re welcome to copy it and tweak for your project. Once you have it written, simply point your coding agent at it.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>pi <span class="s2">"Read plan.md and let me know when I can quit this session"</span>
</code></pre></div></div>

<p>The prompt specifies that contributing agents should be spun up in VMs so you can close your laptop and know things are still progressing.</p>

<h3 id="let-it-start-running">Let it start running</h3>

<p>Eventually <code class="language-plaintext highlighter-rouge">pi</code> or your coding agent will tell you agents are working and you’re good to quit the session. Congrats, you’ve successfully created a code cannon to work on some problem.</p>

<h3 id="check-where-its-at">Check where it’s at</h3>

<p>Regardless of the size of the project, since there may be small features or nits you’d like to include anyways, it’s good to check in after agents began working to verify what it is they’re working on. If you find yourself glossing over the agent descriptions and more crossing your fingers than walking away knowing your progress, you’ve likely depended on the agents too much for your goal.</p>

<h3 id="repeat-running-and-checking-in">Repeat running and checking in</h3>

<p>We found it useful to check in on the swarm similar to checking in with a team during standup but on an admittedly more frequent basis. Rather than provide a prompt to scale up/down the swarm after certain checkpoints, being more hands-on with steering allowed us to also get a clearer understanding of the scope of this project as well.</p>

<h2 id="how-we-rewrote-git-in-zig">How we rewrote git in zig</h2>

<p>Anthropic <a href="https://github.com/anthropics/claudes-c-compiler/issues/1">took a stab</a> at rewriting the C compiler and Cursor <a href="https://github.com/wilsonzlin/fastrender/issues/98">took a stab</a> at rewriting a web browser. It’s not that hard for you to do the same and here’s how we went about rewriting a big open source project with the help of agents!</p>

<h3 id="environment">Environment</h3>

<p>The <a href="https://vers.sh/">Vers VMs</a> spawned had the environment variables injected at startup (so running <code class="language-plaintext highlighter-rouge">pi</code> with instructions will always work).</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">ANTHROPIC_API_KEY</code>: For the LLM powering the coding agent</li>
  <li><code class="language-plaintext highlighter-rouge">VERS_API_KEY</code>: For further orchestration</li>
  <li><code class="language-plaintext highlighter-rouge">GITHUB_API_KEY</code>: A strictly scoped API key for just one repository</li>
</ul>

<h3 id="the-initial-plan">The initial plan</h3>

<p>Below was literally the one markdown file used for the original agent to spin up a swarm.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The goal is to make a modern version control software like git or jj but written in zig

ALL SYSTEMS AND AGENTS MUST use this github -&gt; https://github.com/hdresearch/ziggit.git

For each of the below goals, create a VM and run code like the following

<span class="p">```</span><span class="nl">bash
</span><span class="k">while </span><span class="nb">true </span><span class="k">do
  </span>pi <span class="nt">-run</span> <span class="s2">"GOAL"</span>
end
<span class="p">```</span>

NOTE - pi is running on the VM itself rather than running on the host machine and then ssh'ing commands. This should be done so we can quit this pi session

So agents are just infinitely running since there is always something to improve in a piece of software. Include pi-vers extension so each infinite loop can provision further VMs or agents.
<span class="p">
-</span> first person like jj but does not have a <span class="sb">`jj git`</span> subcommand and instead is drop in replaceable with <span class="sb">`git`</span> so <span class="sb">`ziggit checkout`</span> not <span class="sb">`ziggit git checkout`</span>
<span class="p">-</span> feature compatibility with git (copy over test suite from git source)
<span class="p">-</span> can compile to webassembly
<span class="p">-</span> can yield performance improvements to oven-sh/bun codebase by using directly with zig integration instead of libgit2 or git cli

Maybe wait for some progress before starting on replacing bun's usage of the git cli (which they use over libgit2 for performance reasons, our suspicion is that a modern solution in zig could be better). Every VM should have the env vars <span class="sb">`VERS_API_KEY`</span>, <span class="sb">`ANTHROPIC_API_KEY`</span>, <span class="sb">`GITHUB_API_KEY`</span>. Also use the hdresearch/bun fork with changes so a real PR can be created pointing at oven-sh/bun BUT DO NOT MAKE THIS PR YOURSELF. Provide instructions for a person to validate the benchmark results with ziggit usage first
</code></pre></div></div>

<p>We copied over the <code class="language-plaintext highlighter-rouge">plan.md</code> used for <a href="https://vers.sh/blog/elixir-webassembly-billion-tokens">firebird</a> and the <code class="language-plaintext highlighter-rouge">-run</code> argument is not a real argument, the correct one is <code class="language-plaintext highlighter-rouge">-p</code> but the top-level agent figures it out anyways.</p>

<h3 id="the-produced-agent-loop">The produced agent loop</h3>

<p>From the markdown plan, our local <code class="language-plaintext highlighter-rouge">pi</code> agent created a golden image for the VMs working on the <code class="language-plaintext highlighter-rouge">ziggit</code> codebase to use and configured each agent to have different git commit authors so progress would be identifiable.</p>

<p>Every agent additionally got a <code class="language-plaintext highlighter-rouge">/root/prompt.txt</code> file with that agent’s specific prompt. The agent tasked with covering git’s test suite would have that file populated with contents like <code class="language-plaintext highlighter-rouge">"You are the CORE agent. Run git's test suite and fix CLI bugs."</code> and the agent tasked with improving certain git index functionality would have that file with contents like <code class="language-plaintext highlighter-rouge">"You are the NET-SMART agent. Rewrite idx_writer.zig to be 10x faster."</code>.</p>

<p>Finally, every VM runs the exact same bash loop encompassing the coding agent itself as well as the git cleanups referenced earlier. The below was generated by the top-level pi agent orchestrating these coding processes in VMs for how to define a given agent.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nb">set</span> <span class="nt">-a</span><span class="p">;</span> <span class="nb">source</span> /etc/environment 2&gt;/dev/null<span class="p">;</span> <span class="nb">set</span> +a
<span class="nb">export </span><span class="nv">HOME</span><span class="o">=</span>/root
<span class="nb">export </span><span class="nv">NODE_OPTIONS</span><span class="o">=</span><span class="s2">"--max-old-space-size=256"</span>

<span class="nb">cd</span> /root/myproject <span class="o">||</span> <span class="nb">exit </span>1

<span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do
    </span><span class="nb">echo</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">date</span><span class="si">)</span><span class="s2">: === Starting agent run ==="</span>

    <span class="c"># 1. SYNC — save dirty work, pull latest from other agents</span>
    git add <span class="nt">-A</span>
    git diff <span class="nt">--cached</span> <span class="nt">--quiet</span> <span class="o">||</span> git commit <span class="nt">-m</span> <span class="s2">"auto-save before sync"</span>
    git fetch origin master
    git rebase origin/master <span class="o">||</span> <span class="o">{</span>
        git rebase <span class="nt">--abort</span>
        git reset <span class="nt">--hard</span> origin/master  <span class="c"># nuclear option on conflicts</span>
    <span class="o">}</span>

    <span class="c"># 2. BUILD — rebuild the project</span>
    zig build  <span class="c"># or whatever your build command is</span>

    <span class="c"># 3. RUN PI — the actual agent work</span>
    pi <span class="nt">--no-session</span> <span class="nt">-p</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">cat</span> /root/prompt.txt<span class="si">)</span><span class="s2">"</span>

    <span class="c"># 4. PUSH — commit and push whatever pi did</span>
    git add <span class="nt">-A</span>
    git diff <span class="nt">--cached</span> <span class="nt">--quiet</span> <span class="o">||</span> git commit <span class="nt">-m</span> <span class="s2">"auto-save after pi run"</span>
    <span class="k">for </span>attempt <span class="k">in </span>1 2 3<span class="p">;</span> <span class="k">do
        </span>git pull <span class="nt">--rebase</span> origin master <span class="o">||</span> <span class="o">{</span>
            git rebase <span class="nt">--abort</span>
            git reset <span class="nt">--hard</span> origin/master
        <span class="o">}</span>
        git push origin master <span class="o">&amp;&amp;</span> <span class="nb">break
        sleep </span>5
    <span class="k">done

    </span><span class="nb">sleep </span>10
<span class="k">done</span>
</code></pre></div></div>

<p>It executes every loop by saving work from the prior loop run, pulling in latest changes, rebuilding the project, running the pi agent, and then repeating the same git operations at the end with also pushing. The agent prompts themselves also mention to use git operations for auditability but these git failguards around the agent itself help ensure the agent loop doesn’t get stuck along the way.</p>

<h3 id="meta-note">Meta note</h3>

<p>To reiterate a point at the end of a <a href="#agent-spawned-agents-is-like-being-a-manager-of-managers">another section</a>, the sub-agents aren’t doing anything differently from if you were to be manually starting new agents with their respective prompts yourself. These shouldn’t be doing anything you can’t directly understand, whenever we found ourselves starting with an initial research goal (ie understanding a point of integration before beginning new <a href="#the-produced-agent-loop">loops</a>) and letting the agent in front of me handle the rest, we’ve ended up with a mess to clean up.</p>

<p>Similar to how LLMs can be poor at writing configuration files, we’d guess complex integrations fall under a similar category of “problems LLMs do a lot better with a human around” and, should you be working on one of these tasks, make sure every detail relevant to your intended prompt or plan for an agent to carry out is in the context you hit <code class="language-plaintext highlighter-rouge">Enter</code> on.</p>

<h3 id="what-it-cost">What it cost</h3>

<p>At the end of this crunch which consisted of nearly a week and ~13 billion tokens, we successfully created a rewrite of git in zig. If you were to do that as a human, say writing a new <code class="language-plaintext highlighter-rouge">git</code> of your own, and you were to work towards 100% test coverage, you’d be in for a world of pain.</p>

<p><img src="https://vers.sh/hdr_legacy/images/git-tokens.png" style="width: 100%" /></p>

<p>The git CLI test suite consists of 21,329 individual assertions for various git subcommands (that way we can be certain <code class="language-plaintext highlighter-rouge">ziggit</code> does suffice as a drop-in replacement for <code class="language-plaintext highlighter-rouge">git</code>). If it took a person four minutes to write enough functionality to pass each test (overlooking some tests being more complex than others), that’d amount to 85,316 minutes total, or about two months! And that’s without sleeping or eating included in the number.</p>

<p>While we only got through <a href="#the-final-results">part of the overall test suite</a>, that’s still the equivalent of a month’s worth of straight developer work (again, without sleep or eating factored in).</p>

<h3 id="the-final-results">The final results</h3>

<h4 id="bun-improvements">bun improvements</h4>

<table>
  <thead>
    <tr>
      <th>Operation</th>
      <th>macOS arm64 (M4)</th>
      <th>x86_64 Linux VM</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">findCommit</code></td>
      <td><strong>85.4x</strong> win</td>
      <td><strong>6.3x</strong> win</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cloneBare</code></td>
      <td><strong>7.3x</strong> win</td>
      <td><strong>34.3x</strong> win</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cloneBare</code> + <code class="language-plaintext highlighter-rouge">findCommit</code> + <code class="language-plaintext highlighter-rouge">checkout</code></td>
      <td><strong>~10x</strong> win</td>
      <td><strong>~30x</strong> win</td>
    </tr>
  </tbody>
</table>

<p>The <code class="language-plaintext highlighter-rouge">bun</code> team has already <a href="https://github.com/oven-sh/bun/blob/3ed4186bc8db8357c670307f192991bfc263f141/docs/runtime/templating/create.mdx?plain=1#L267">tested using git’s C library</a> and found it to be consistently slower hence resorting to literally executing the <code class="language-plaintext highlighter-rouge">git</code> CLI when performing <code class="language-plaintext highlighter-rouge">bun install</code>. With <code class="language-plaintext highlighter-rouge">ziggit</code>, it becomes possible to see upward of <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#macos-arm64-releasefast-20-iterations"><strong>100x speedups</strong></a> for some git operations.</p>

<p>Tested on an M4 Macbook with 24gb of RAM across multiple runs, it scored an average of <strong>85.4x</strong> speedup for <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#findcommit-rev-parse-head"><code class="language-plaintext highlighter-rouge">findCommit</code></a>, <strong>7.3x</strong> speedup for <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#clonebare-local-bare-clone"><code class="language-plaintext highlighter-rouge">cloneBare</code></a>, and a <strong>~10x</strong> speedup for the <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#full-workflow-clonebare--findcommit--checkout">entire workflow</a> comprising of git operations. In a x86_64 Linux VM with 8gb of RAM, it scored an average of <strong>6.3x</strong> speedup for <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#findcommit"><code class="language-plaintext highlighter-rouge">findCommit</code></a>, <strong>34.3x</strong> speedup for <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#clonebare"><code class="language-plaintext highlighter-rouge">cloneBare</code></a>, and a <strong>~30x</strong> speedup for the <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#full-workflow">full workflow</a>.</p>

<p>When evaluating the complete <code class="language-plaintext highlighter-rouge">bun install</code> improvements, it came out speed-wise to about the same as the existing <code class="language-plaintext highlighter-rouge">git</code> usage (due to networking being the big bottleneck time-wise despite more cases being slightly faster with <code class="language-plaintext highlighter-rouge">ziggit</code> over multiple benchmarks). <em>Except</em>, it’s done in 100% zig <em>and</em> those internal improvements pile up as projects <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#why-e2e-shows-modest-speedups-despite-10-85-library-speedups">consist of more git dependencies</a>. All in all, it seems like a sensible upstream contribution.</p>

<h4 id="git-drop-in">git drop-in</h4>

<table>
  <thead>
    <tr>
      <th>Benchmark</th>
      <th>ziggit vs git</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>arm64 Mac (small repos)</td>
      <td><strong>&gt;4x</strong> win</td>
    </tr>
    <tr>
      <td>arm64 Mac (large repos)</td>
      <td><strong>&gt;4x</strong> win</td>
    </tr>
    <tr>
      <td>Best commands</td>
      <td>up to <strong>10x</strong> win</td>
    </tr>
  </tbody>
</table>

<p>In addition to covering enough functionality to replace bun’s usage of the <code class="language-plaintext highlighter-rouge">git</code> CLI, <code class="language-plaintext highlighter-rouge">ziggit</code> covers enough subcommands and arguments to be a viable drop-in replacement for git with numerous performance improvements. While there are codepaths where the two are at <strong>1x</strong> performance comparisons, it’s remarkable that a modern rewrite in a modern programming language was able to reach that level <em>and</em> even get up to <strong>10x</strong> speedup for <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#macos-arm64--large-repo-ziggit-itself-2367-commits-150-files">some commands</a>!</p>

<p>While <code class="language-plaintext highlighter-rouge">git</code> itself has had much more development and optimizations for x86_64 Linux, <code class="language-plaintext highlighter-rouge">ziggit</code>’s performance really outshines <code class="language-plaintext highlighter-rouge">git</code> when measuring on an arm64 Macbook. On our macbook, it’s across the board more than <strong>4x</strong> faster than <code class="language-plaintext highlighter-rouge">git</code> in both <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#macos-arm64--small-repo-51-commits-100-files">smaller repositories</a> as well as <a href="https://github.com/hdresearch/ziggit/blob/5d3deb361f03d4aefef29426cf333782fc05d7cf/BENCHMARKS.md#macos-arm64--large-repo-ziggit-itself-2367-commits-150-files">larger ones</a>.</p>

<p>Of course, <code class="language-plaintext highlighter-rouge">ziggit</code> comes with <strong>git-lfs</strong> support as well and a useful <a href="#succinct-mode">succinct mode</a> meant for agents working in new or existing git projects to save significantly in tokens!</p>

<h4 id="webassembly">WebAssembly</h4>

<table>
  <thead>
    <tr>
      <th>Metric</th>
      <th>ziggit</th>
      <th>wasm-git</th>
      <th>Result</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Binary size</td>
      <td>148kb (55kb compressed)</td>
      <td>806kb</td>
      <td><strong>5.4x</strong> win</td>
    </tr>
    <tr>
      <td>Named exports</td>
      <td>68</td>
      <td>8</td>
      <td><strong>8.5x</strong> win</td>
    </tr>
  </tbody>
</table>

<p>Currently, there’s a <a href="https://github.com/petersalomonsen/wasm-git">wasm-git</a> project which compiles <a href="https://github.com/petersalomonsen/wasm-git?tab=readme-ov-file#compatibility">git’s C library</a> directly to WASM and comes out to 806kb large. <code class="language-plaintext highlighter-rouge">ziggit</code>, when compiled to WASM, produces a binary that’s only 148kb big. That’s <strong>5.4x</strong> smaller already on its own and then it can get down to just 55kb when compressed, making it more portable and accessible.</p>

<p>Additionally, <code class="language-plaintext highlighter-rouge">ziggit</code>’s WebAssembly binary provides 68 named distinct exports (<code class="language-plaintext highlighter-rouge">ziggit_init</code>, <code class="language-plaintext highlighter-rouge">ziggit_clone_bare</code>, <code class="language-plaintext highlighter-rouge">ziggit_diff</code>, <code class="language-plaintext highlighter-rouge">ziggit_log</code>, etc) in contrast to <code class="language-plaintext highlighter-rouge">wasm-git</code>’s 8 obfuscated exports (X, Y, Z, _, $, aa, ba, ca) which are Emscripten-compiled C bindings. Nonetheless, talk’s cheap so you can go ahead and clone an open source repository <a href="https://vers.sh/ziggit-demo">in our web demo</a>.</p>

<h4 id="succinct-mode">Succinct mode</h4>

<p>Inspired by <a href="https://github.com/rtk-ai/rtk">rtk</a>, a CLI proxy which reduces LLM token consumption by <strong>60-90%</strong>, <code class="language-plaintext highlighter-rouge">ziggit</code> also includes a “succinct mode” that’s enabled by default and dramatically slims down outputs. For example, the below:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s2">"chore: add another file"</span>
<span class="o">[</span>master b6eeb42] chore: add staged file
1 file changed, 1 insertion<span class="o">(</span>+<span class="o">)</span>
</code></pre></div></div>

<p>Becomes the below:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ziggit commit <span class="nt">-m</span> <span class="s2">"chore: add another file"</span>
ok master 640fe38 <span class="s2">"chore: add another file"</span>
</code></pre></div></div>

<p>Or compare the below difference between <code class="language-plaintext highlighter-rouge">git status</code> and <code class="language-plaintext highlighter-rouge">ziggit status</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--- normal ---                              --- succinct ---
On branch master                            * master
                                             + Staged: 1 files
Changes to be committed:                      staged.txt
  (use "git restore --staged ..." ...)       ~ Modified: 1 files
        new file:   staged.txt                 README.md
 Changes not staged for commit:
  (use "git add ..." ...)
  (use "git restore ..." ...)
        modified:   README.md
</code></pre></div></div>

<p>Succinct mode is turned on by default and can be toggled off by passing <code class="language-plaintext highlighter-rouge">--no-succinct</code> like so.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ziggit <span class="nt">--no-succinct</span> status
</code></pre></div></div>

<p>Or by setting the <code class="language-plaintext highlighter-rouge">GIT_SUCCINCT</code> environment variable.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">GIT_SUCCINCT</span><span class="o">=</span>0 ziggit status
</code></pre></div></div>

<h2 id="theory">Theory</h2>

<p>Now, why does any of this work? Here’s our guess having done a similar thing before when making a modern toolkit to <a href="https://vers.sh/blog/elixir-webassembly-billion-tokens">bridge Elixir and WebAssembly</a>.</p>

<h3 id="agent-spawned-agents-is-like-being-a-manager-of-managers">Agent spawned agents is like being a manager of managers</h3>

<p>Having direct reports who work with you is vastly different from working with reports who themselves have reports.</p>

<p><img src="https://vers.sh/hdr_legacy/images/manager_vs_manager_of_managers_clean.svg" alt="Two org charts of direct reports vs manager of managers" /></p>

<p>Normally, when you’re working in an organization of people, you need to be mindful of the balance and delegation of tasks; this has to do with everyone’s experiences as well as APMs. When you work with coding agents, you could sit and create a coding agent for every individual task <em>or</em> you could have an agent (which itself has a high APM) be the one doing the orchestration:</p>

<p><img src="https://vers.sh/hdr_legacy/images/agentic_coding_orchestration.svg" alt="Org chart of human prompting an agent to spawn coding agents" /></p>

<p>But, really, this wasn’t a “hands off the wheel” project where we hit <code class="language-plaintext highlighter-rouge">Enter</code> once and left the laptop; although we got sleep in the process. Instead, this was more like doing exactly what we would have done if we had a row of laptops on a table and we’re typing on each one except there’s an agent to do the menial part of setting up subsequent coding agents:</p>

<p><img src="https://vers.sh/hdr_legacy/images/augmented_human_to_coding_agents_v2.svg" alt="Chart of human being augmented to aid with orchestrating agents" /></p>

<p>For the early part of the work, we prompted the top-level agent to create certain agents for the initial scaffold (in this case: core git functionality as well as identifying where to place the zig code in Bun’s codebase). Once there was enough groundwork laid out, we directed the top-level agent to spawn different agents we knew could work in parallel (ie one was focusing on WebAssembly capability, one was focusing on the exact git functionalities to rewrite to 100% Zig for Bun).</p>

<p>For scenarios where we figured one agent was not going to fulfill some capability in a reasonable amount of time (mind you, this stuff is eating up billions of tokens so not like it’s absurdly unreasonable in the first place), we’d have multiple agents working in the same part of the codebase except the logic wrapping the agent itself (both in the prompt and in literal shell scripts), we use git to rebase or stash or push changes along the way. This both ensures agents don’t tunnel vision themselves into stuff that’s never pushed and agents can be failure tolerant when one gets a task that was already handled by another agent.</p>

<h3 id="why-we-think-this-works">Why we think this works</h3>

<p>We’ve successfully applied this approach before when <a href="https://vers.sh/blog/elixir-webassembly-billion-tokens">bridging Elixir and WebAssembly</a> and have a guess as to why this works. To explain, let’s talk about making a peanut butter and jelly sandwich.</p>

<p>For context, one of our favorite examples for introducing computer science is the exercise of <a href="https://youtu.be/okkIyWhN0iQ">writing instructions for how to prepare a peanut butter and jelly sandwich</a>. It’s a staple I remember from <a href="https://www.edx.org/cs50">Harvard’s CS50</a> and have done enjoyably a number of times when I was teaching others how to code pre-LLMs.</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/okkIyWhN0iQ?si=lqCXa4ZPh232v1bC" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>The way it goes is you have all the ingredients and tools you’d use to prepare a PB&amp;J (bread, peanut butter, jelly, plates, so on) as well as something to write on and something to write with (such as blackboard, whiteboard, paper, text editor). You begin by instructing the group to provide (so it can be written down) the instructions for preparing a PB&amp;J while, along the way, you follow instructions extremely literally such that a sandwich never gets made (unless you’re nice about it). The goal isn’t to demoralize your students into thinking they can’t define steps but more to emphasize how “dumb” computers can be and how explicit code needs to be for a program to do what you expect.</p>

<p>If you prompt an LLM to make a PB&amp;J, assuming it has access to whatever’s needed in the real world with robot arms plus all the cool hijinks, you’ll likely end up with something much like how you can prompt a coding agent to make some program and it will likely end up with <em>something</em>. If you want to ensure that every sandwich made uses apricot jam, that’s something to specify in the instructions. If you want to ensure some web app generation always uses a certain component library, that’s something to specify in the instructions as well. LLMs are great because they can <em>do things</em> but whichever details you care about must be specified similar to how a human doing the PB&amp;J exercise would need the orientation of the knife and so on to be specified.</p>

<p>The peanut butter and jelly sandwich example works for standard coding because computers need programs to be precise. The example also works for LLMs coding because agents need prompts to be precise. To tie together how one could see that coding agents have the potential to solve a hefty number of engineering problems, let’s consider two things that we know today LLMs are able to do:</p>

<p>1) Build out an initial MVP or prototype</p>
<ul>
  <li>While this was an early critique for coding applications of LLMs (since they can’t do “real engineering work”), it’s worth admitting this does knock off legitimate work that’d otherwise take a person time to do.
2) Targeted optimizations that are verified by the LLM</li>
  <li>Google showed this already with <a href="https://deepmind.google/blog/alphaevolve-a-gemini-powered-coding-agent-for-designing-advanced-algorithms/">AlphaEvolve</a> and, in a more broad way,  the <a href="https://greylock.com/greymatter/the-deepseek-moment/">Deepseek moment</a> shows this point further. Rather than throwing hands up in defeat and running LLMs over and over like <a href="https://en.wikipedia.org/wiki/Infinite_monkey_theorem">monkeys on typewriters</a>, giving LLMs access to the metrics a human would be trying to steer towards in the first place lets them self-guide till they get the job done.</li>
</ul>

<p>By being able to both legitimately start a project as well as improve it in the directions desired, putting aside the verbosity needed in the prompt or time needed to process, LLMs and coding agents have the capability of tackling a “real” number of engineering problems. It’s not about replacing humans or finding things humans can’t do at all, it’s about overall coordination in the vein of enriched productivity.</p>

<p>At this point, we have all the fundamental pieces for why this approach is productive: meaningfully organizing and directing coding agents with a “top-level” agent doing the administrative work for you. Being able to work with the top-level agent and improve sub-agent prompts or loops also let a deployed agent not be the end all be all but instead iterative.</p>

<p>What was funny about steering this system of agents is it was reminiscent of seeing demands of engineering teams evolve over time like the startups we’ve been at; when the group needs to focus on a <a href="https://blog.pragmaticengineer.com/uber-app-rewrite-yolo/">refactor</a> or tasks can be <a href="https://www.atlassian.com/agile/agile-at-scale/spotify">divided in parallel</a>, agents can be redirected towards something or spawned/killed according to the codebase’s demands. The point here being there wasn’t a single organizational structure or scaffold which was the “best”, our orchestration was more dynamic as I went along with the project.</p>

<p>An important note about organizations of these agents we’ll add is <a href="https://www.laws-of-software.com/laws/kernighan/">Kernighan’s Law</a>.</p>

<blockquote>
  <p>Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it, how will you ever debug it?</p>
</blockquote>

<p>If you point the top-level agent at the task of figuring out the most clever tricks possible, you’ll end up with a mess of agents and a <em>lot</em> of token burn for no good reason.</p>

<p>We don’t yet have a prescriptive solution for this but the rule of thumb we’d state is, at any given point in time, you should be able to see a list of running agents and understand the progress they’re making. If you find yourself in a spot where you wouldn’t know where to begin steering, you’ve likely leaned too much on the agents to do something you were responsible for.</p>

<p>Hack the planet.</p>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[Digest]]></summary></entry></feed>