<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Posts Tagged "ocaml" on Alex Leighton's Blog</title>
  <id>https://alexleighton.com/posts/tags/ocaml-tag-feed.xml</id>
  <link href="https://alexleighton.com/posts/tags/ocaml-tag-feed.xml" rel="self" />
  <link href="https://alexleighton.com/posts/tags/ocaml.html" />
  <updated>2026-08-17T01:58:50.026853084Z</updated>
  <author>
    <name>Alex Leighton</name>
    <uri>https://alexleighton.com/</uri>
  </author>
  <icon>https://alexleighton.com/static/icon-dino.png</icon>
  <logo>https://alexleighton.com/static/icon-dino.png</logo>
  
  <entry>
    <title>termaid Maintenance</title>
    <id>https://alexleighton.com/posts/2026-08-16-termaid-maintenance.html</id>
    <link href="https://alexleighton.com/posts/2026-08-16-termaid-maintenance.html" />
    <published>2026-08-16T23:45:00Z</published>
    <updated>2026-08-16T23:45:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Planning for upstream drift.</p><p>Published on <span title="2026-08-16T23:45:00Z">2026-08-16</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Planning for upstream drift.</h3><p>Published on <span title="2026-08-16T23:45:00Z">2026-08-16</span><br>Tags: article, commentary, llm, ocaml, software-eng, software-eng-auto</p><p>termaid, my <a href="../../../posts/2026-07-20-termaid.html">OCaml port of <code>mermaid.rs</code></a>, is <a href="https://ocaml.org/p/termaid/latest">available on opam</a>! 🙌 With that release, the work shifts to maintenance.</p>
<h2 id="upstream-check">Upstream Check <a class="heading-anchor" href="#upstream-check" aria-hidden="true">#</a></h2>
<p>Given that this is a machine-driven port of an existing piece of code, I wanted to set up some infrastructure so that I'd catch any upstream bug fixes or feature enhancements. So I added an <a href="https://github.com/alexleighton/termaid/blob/main/.github/workflows/upstream-check.yml"><code>upstream-check.yml</code> GitHub Actions workflow</a> to execute <a href="https://github.com/alexleighton/termaid/blob/main/scripts/check_upstream.sh"><code>check_upstream.sh</code></a> and file an issue when it spots a change. The script uses the commit and blob references in <a href="https://github.com/alexleighton/termaid/blob/main/oracle/UPSTREAM"><code>oracle/UPSTREAM</code></a> to determine changes against upstream. When <a href="https://github.com/alexleighton/termaid/actions">the check fires</a>, I'll ask an agent to update the oracle to the new upstream <code>HEAD</code>, use the oracle to generate test cases relevant to the changes, and adjust the OCaml code to make the tests pass.</p><p>...<br><a href="https://alexleighton.com/posts/2026-08-16-termaid-maintenance.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>termaid</title>
    <id>https://alexleighton.com/posts/2026-07-20-termaid.html</id>
    <link href="https://alexleighton.com/posts/2026-07-20-termaid.html" />
    <published>2026-07-21T01:15:00Z</published>
    <updated>2026-07-21T01:15:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Terminal Mermaid rendering for OCaml.</p><p>Published on <span title="2026-07-21T01:15:00Z">2026-07-21</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Terminal Mermaid rendering for OCaml.</h3><p>Published on <span title="2026-07-21T01:15:00Z">2026-07-21</span><br>Tags: article, llm, ocaml, rust, software-eng, software-eng-auto</p><p>I saw <a href="https://simonwillison.net/2026/Jul/16/grok-mermaid">this post</a> from Simon Willison about running <code>mermaid.rs</code> — a utility from Grok Build, xAI's newly open-sourced coding agent harness, as WebAssembly in the browser. The output looks really nice, the Rust code is very self-contained, and I thought it would be interesting to use an agent to convert it to OCaml. The outcome is <a href="../../../projects/termaid.html"><code>termaid</code></a>, a minimal-dependency OCaml library for rendering Mermaid diagrams.</p>
<h2 id="example">Example <a class="heading-anchor" href="#example" aria-hidden="true">#</a></h2>
<p>The package contains a minimal binary that renders a diagram from stdin, alongside the library itself, for use in other programs. Supported diagram types: flowchart/graph, stateDiagram, classDiagram, erDiagram, and sequenceDiagram.</p>
<pre><code class="language-shell">$ termaid &lt;&lt;'EOF'
sequenceDiagram
  A-&gt;&gt;B: ping
  B--&gt;&gt;A: pong
EOF
┌───┐  ┌───┐
│ A │  │ B │
└─┬─┘  └─┬─┘
  │      │
  │ ping │
  ├─────▶│
  │      │
  │ pong │
  │◄╌╌╌╌╌┤
  │      │
┌─┴─┐  ┌─┴─┐
│ A │  │ B │
└───┘  └───┘
</code></pre><p>...<br><a href="https://alexleighton.com/posts/2026-07-20-termaid.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>No Docs: macOS-only Packages on OCaml.org</title>
    <id>https://alexleighton.com/posts/2026-07-09-no-docs-macos-only-packages-on-ocaml-org.html</id>
    <link href="https://alexleighton.com/posts/2026-07-09-no-docs-macos-only-packages-on-ocaml-org.html" />
    <published>2026-07-09T14:30:00Z</published>
    <updated>2026-07-09T14:30:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Publishing documentation for osx-keychain.</p><p>Published on <span title="2026-07-09T14:30:00Z">2026-07-09</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Publishing documentation for osx-keychain.</h3><p>Published on <span title="2026-07-09T14:30:00Z">2026-07-09</span><br>Tags: article, documentation, macos, ocaml, software-eng</p><p><strong>The OCaml.org opam documentation process does not trigger for macOS-only packages, leaving ones like <code>osx-keychain</code> labeled as "⚠️ No Docs".</strong></p>
<h2 id="️-no-docs">⚠️ No Docs <a class="heading-anchor" href="#️-no-docs" aria-hidden="true">#</a></h2>
<p>I couldn't find a mention of this in the official documentation. In 2022, <a href="https://discuss.ocaml.org/t/v3-ocaml-org-we-are-live/9747">OCaml.org started serving automatically generated documentation</a> for applicable opam packages, making use of a new <a href="https://icfp21.sigplan.org/details/ocaml-2021-papers/15/A-Multiverse-of-Glorious-Documentation"><code>ocaml-docs-ci</code> documentation pipeline</a>. Earlier this year, <a href="https://tarides.com/blog/2026-01-29-ocaml-org-now-uses-odoc-3-what-s-new/">OCaml.org upgraded to odoc 3</a>, accompanied by <a href="https://jon.recoil.org/blog/2025/04/ocaml-docs-ci-and-odoc-3.html">updates to <code>ocaml-docs-ci</code></a> for odoc 3 compatibility.</p>
<p><a href="https://github.com/ocurrent/ocaml-docs-ci"><code>ocaml-docs-ci</code></a> explicitly targets <a href="https://github.com/ocurrent/ocaml-docs-ci/blob/main/src/lib/platform.ml#L9-L16">Linux platforms</a> only. Because <code>osx-keychain</code> declares <a href="https://github.com/alexleighton/osx-keychain/blob/main/osx-keychain.opam#L41"><code>available: [ os = "macos" ]</code></a>, opam treats it as unavailable on ocaml-docs-ci's Linux workers, filtering it out of the set of packages docs-ci considers buildable before even attempting a build.</p><p>...<br><a href="https://alexleighton.com/posts/2026-07-09-no-docs-macos-only-packages-on-ocaml-org.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>osx-keychain Released on Opam</title>
    <id>https://alexleighton.com/posts/2026-07-04-osx-keychain-on-opam.html</id>
    <link href="https://alexleighton.com/posts/2026-07-04-osx-keychain-on-opam.html" />
    <published>2026-07-04T22:00:00Z</published>
    <updated>2026-07-04T22:00:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>My first Opam-published package, and why I built it.</p><p>Published on <span title="2026-07-04T22:00:00Z">2026-07-04</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>My first Opam-published package, and why I built it.</h3><p>Published on <span title="2026-07-04T22:00:00Z">2026-07-04</span><br>Tags: article, macos, ocaml, security, software-eng, static-site</p><p>The <a href="../../../projects/osx-keychain.html"><code>osx-keychain</code></a> library I built to natively pull credentials from the macOS keychain was accepted into Opam and is now publicly available as a dependency, a first for me 🥳. Here is some context on why I made it:</p>
<p>There are two modes in which I interact with AWS for the alexleighton.com static site: during site upload, and during image upload.</p>
<h2 id="site-upload">Site Upload <a class="heading-anchor" href="#site-upload" aria-hidden="true">#</a></h2>
<p>I used to rely on GitHub Actions to build and deploy all commits on the <code>main</code> branch, but it ended up being the worst of all worlds. Reinstalling opam, dune, the OCaml compiler, and all dependencies often took over 10 minutes, regardless of the Actions-caching I tried to apply. My static site is a private repository, so GitHub eventually started nagging about reaching the 1000 monthly build hours threshold for private repos. As a result I turned off the automation and have been building/uploading my site straight off my laptop using a bash script that does what the Action did.</p><p>...<br><a href="https://alexleighton.com/posts/2026-07-04-osx-keychain-on-opam.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>Changelog: Images from a CDN</title>
    <id>https://alexleighton.com/posts/2026-06-30-changelog-cdn-images.html</id>
    <link href="https://alexleighton.com/posts/2026-06-30-changelog-cdn-images.html" />
    <published>2026-07-01T05:00:00Z</published>
    <updated>2026-07-01T05:00:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Shrinking deployment size 97% by serving images from CloudFront.</p><p>Published on <span title="2026-07-01T05:00:00Z">2026-07-01</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Shrinking deployment size 97% by serving images from CloudFront.</h3><p>Published on <span title="2026-07-01T05:00:00Z">2026-07-01</span><br>Tags: changelog, ocaml, static-site</p><p>Image variants are now served from a CDN at <code>img.alexleighton.com</code> instead of being bundled into the site and served by Amplify.</p>
<p>It seems I <a href="../../../posts/2025-09-23-changelog-images.html">never came back</a> to detail the custom image system underlying the images on this static site, so let's begin with the before, to motivate the after. The image system comprises a private S3 bucket, local filesystem cache, image "database", and an image system library.</p>
<p>The image system library is written in OCaml and integrated into the static site generator, to allow image reference resolution. A separate executable fronts the image system, to allow uploading and syncing of images during post authoring. Image upload through the system generates a number of variants of different sizes and formats (<code>jpeg</code> as the lowest common denominator, <code>webp</code> for most browsers, <code>avif</code> for modern browsers), assigns a <a href="../../../posts/2025-11-29-typeids-for-knowledge-bases.html">TypeId</a> to each image, uploads the images to the source-of-truth private S3 bucket, adds them to the filesystem cache, and adds corresponding rows to the CSV image database. The site generator reads the image database to dereference image TypeIds into source URLs, copies every referenced image into the output directory, and includes those images in the release artifact.</p><p>...<br><a href="https://alexleighton.com/posts/2026-06-30-changelog-cdn-images.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>OCaml Runtime Translated to Rust</title>
    <id>https://alexleighton.com/posts/2026-06-14-ocaml-runtime-translated-to-rust.html</id>
    <link href="https://alexleighton.com/posts/2026-06-14-ocaml-runtime-translated-to-rust.html" />
    <published>2026-06-15T03:30:00Z</published>
    <updated>2026-06-15T03:30:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Near-parity performance, minimal human guidance.</p><p>Published on <span title="2026-06-15T03:30:00Z">2026-06-15</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Near-parity performance, minimal human guidance.</h3><p>Published on <span title="2026-06-15T03:30:00Z">2026-06-15</span><br>Tags: commentary, llm, ocaml, rust, software-eng, software-eng-auto</p><p>Michael Bacarella has rewritten the OCaml runtime, using a coding agent to translate the existing C code to Rust line by line.</p>
<blockquote>
<p><a href="https://discuss.ocaml.org/t/a-line-by-line-translation-of-the-ocaml-runtime-from-c-to-rust/18247/1"><strong>Michael Bacarella</strong> on 2026-06-09</a>:</p><p>The headline results are "eh, just about parity, kinda"</p>
<div role="region"><table>
<tbody><tr>
<th>Runtime</th>
<th>Bytecode (cycles vs C)</th>
<th>Native (cycles vs C)</th>
</tr>
<tr>
<td>C (trunk)</td>
<td>1.00x: (baseline)</td>
<td>1.00x (baseline)</td>
</tr>
<tr>
<td>Rust (stable)</td>
<td>1.44x, slower than C</td>
<td>~1.05x (range: 0.87-1.13)</td>
</tr>
<tr>
<td>Rust (nightly) (ETCs)</td>
<td>0.91x, faster than C</td>
<td>same as Rust stable</td>
</tr>
</tbody></table></div></blockquote>
<p>Michael guided a single Claude Opus 4.7 agent through a straightforward translation plan, over the course of seven days. I appreciate this result in automatic software engineering, applied to my favorite programming language. It is one more piece of evidence for the new future of software engineering we find ourselves in: a correct result on a large, non-trivial program, with minimal engineering guidance. Of note: once again, comprehensive test suites are worth their weight in gold when translating large programs.</p><p>...<br><a href="https://alexleighton.com/posts/2026-06-14-ocaml-runtime-translated-to-rust.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>Use Your Preferred Technology</title>
    <id>https://alexleighton.com/posts/2026-03-11-use-your-preferred-technology.html</id>
    <link href="https://alexleighton.com/posts/2026-03-11-use-your-preferred-technology.html" />
    <published>2026-03-11T13:30:00Z</published>
    <updated>2026-03-11T13:30:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Re: Perhaps not Boring Technology after all</p><p>Published on <span title="2026-03-11T13:30:00Z">2026-03-11</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Re: Perhaps not Boring Technology after all</h3><p>Published on <span title="2026-03-11T13:30:00Z">2026-03-11</span><br>Tags: commentary, llm, ocaml, software-eng</p><blockquote>
<p><a href="https://simonwillison.net/2026/Mar/9/not-so-boring/"><strong>Simon Willison</strong> on 2026-03-09</a>:</p><p>Drop a coding agent into any existing codebase that uses libraries and tools that are too private or too new to feature in the training data and my experience is that it works just fine—the agent will consult enough of the existing examples to understand patterns, then iterate and test its own output to fill in the gaps.</p></blockquote>
<p>This is my experience as well. Two years ago (gpt-4o, sonnet-3.5), there was a noticeable difference in the "smoothness" of the OCaml code generated by agents, when compared to generated Python code. The Python code was simpler, more clever, more easily involved various libraries, while the OCaml code had complicated compound expressions, unfortunate nesting (all helper functions defined inside the current function via let-binding instead of deduplicating into the file or across files), and sometimes simply failed to be written in complex situations involving <a href="https://ocaml.org/docs/functors">Functors</a> or circular module definitions or using popular libraries (without handing the agent interface files).</p><p>...<br><a href="https://alexleighton.com/posts/2026-03-11-use-your-preferred-technology.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>KBS: Fast Forward</title>
    <id>https://alexleighton.com/posts/2026-02-28-kbs-fast-forward.html</id>
    <link href="https://alexleighton.com/posts/2026-02-28-kbs-fast-forward.html" />
    <published>2026-03-01T03:45:00Z</published>
    <updated>2026-03-01T03:45:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Code generation on cruise control.</p><p>Published on <span title="2026-03-01T03:45:00Z">2026-03-01</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Code generation on cruise control.</h3><p>Published on <span title="2026-03-01T03:45:00Z">2026-03-01</span><br>Tags: article, llm, ocaml, python, software-eng, software-eng-auto</p><p>On today's episode: a lot of code. The previous work prepared the project codebase to guide agents in generating good quality code, which is what we did.</p>
<h2 id="update-resolve-archive-commands">Update, Resolve, Archive Commands <a class="heading-anchor" href="#update-resolve-archive-commands" aria-hidden="true">#</a></h2>
<p>After the <code>Kb_service</code> module was broken apart, the coding agent easily generated full verticals for update, resolve, and archive commands. The process I'm following:</p>
<ul>
<li>Work with the agent to peel off a chunk of functionality from <a href="https://github.com/alexleighton/knowledge-bases/blob/0eb55844acc47bb847f6d8063a9bc1b4c7689e4b/docs/product-requirements.md"><code>docs/product-requirements.md</code></a>.</li>
<li>Then write a <a href="https://github.com/alexleighton/knowledge-bases/blob/0eb55844acc47bb847f6d8063a9bc1b4c7689e4b/prompts/activities/implementation-plan.md"><code>prompts/activities/implementation-plan.md</code></a> for building the functionality.</li>
<li>After the code is generated, I review the code, as well as the agent (<a href="https://github.com/alexleighton/knowledge-bases/blob/0eb55844acc47bb847f6d8063a9bc1b4c7689e4b/prompts/activities/code-review.md"><code>prompts/activities/code-review.md</code></a>), and apply changes for all issues.</li>
</ul>
<p>Related commit: <a href="https://github.com/alexleighton/knowledge-bases/commit/0eb55844acc47bb847f6d8063a9bc1b4c7689e4b"><code>0eb5584</code></a> — feat: add update, resolve, and archive commands</p><p>...<br><a href="https://alexleighton.com/posts/2026-02-28-kbs-fast-forward.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>KBS: List and Show</title>
    <id>https://alexleighton.com/posts/2026-02-25-kbs-list-and-show.html</id>
    <link href="https://alexleighton.com/posts/2026-02-25-kbs-list-and-show.html" />
    <published>2026-02-26T05:00:00Z</published>
    <updated>2026-02-26T05:00:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Code generation picks up speed.</p><p>Published on <span title="2026-02-26T05:00:00Z">2026-02-26</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Code generation picks up speed.</h3><p>Published on <span title="2026-02-26T05:00:00Z">2026-02-26</span><br>Tags: article, llm, ocaml, software-eng, software-eng-auto</p><p>Today's log consists mostly of code generation. We've laid enough foundation that new functionality is easily produced.</p>
<h2 id="list-functionality">List Functionality <a class="heading-anchor" href="#list-functionality" aria-hidden="true">#</a></h2>
<p>The functionality for <code>bs list</code> is well defined after the work for the <a href="../../../posts/2026-02-24-kbs-nesting-and-ideation.html">previous dev log</a>. I think the experiment of directing the agents from the requirements document rather than my own intuition of what to tackle was successful, though perhaps only at the scale of a project like Knowledge Bases. The coding agent was able to select a reasonable chunk of functionality to peel off and turn into an implementation plan, with a different agent generating the implementation.</p>
<h2 id="flaky-tests">"Flaky" Tests <a class="heading-anchor" href="#flaky-tests" aria-hidden="true">#</a></h2><p>...<br><a href="https://alexleighton.com/posts/2026-02-25-kbs-list-and-show.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>KBS: Nesting and Ideation</title>
    <id>https://alexleighton.com/posts/2026-02-24-kbs-nesting-and-ideation.html</id>
    <link href="https://alexleighton.com/posts/2026-02-24-kbs-nesting-and-ideation.html" />
    <published>2026-02-24T14:30:00Z</published>
    <updated>2026-02-24T14:30:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>More scaffolding before future work.</p><p>Published on <span title="2026-02-24T14:30:00Z">2026-02-24</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>More scaffolding before future work.</h3><p>Published on <span title="2026-02-24T14:30:00Z">2026-02-24</span><br>Tags: article, llm, ocaml, software-eng, software-eng-auto</p><p>Today's changes follow on from the previous post's "next steps": nesting and ideation.</p>
<h2 id="nesting">Nesting <a class="heading-anchor" href="#nesting" aria-hidden="true">#</a></h2>
<p>I worked with the agent to find code with deep nesting, decide how to flatten it, and generalize the approach into suggestions for a <a href="https://github.com/alexleighton/knowledge-bases/blob/faf049e4a4fecaf8a04552e505a178ed29c2299f/docs/lib/principles.md#3-low-nesting-depth">nesting principle</a>. We then applied that principle across the codebase, and I think we were successful as the diff in <a href="https://github.com/alexleighton/knowledge-bases/commit/a05f2ea7eaa69805e88e395082dbfe57d29198d2">the refactor commit</a> is noticeably flatter.</p>
<blockquote>
<p><strong>4. Nesting Depth</strong></p>
<p>Keep expressions nested at most two or three levels deep. Deeply nested code
is hard to follow because the reader must hold every enclosing context in their
head at once. When nesting grows, treat it as a signal that the code should be
restructured.</p>
<p>Approaches for reducing nesting:</p>
<ul>
<li>Use monadic result operators (<code>let*</code>, <code>let+</code>). ...</li>
<li>Extract resource-management wrappers. ...</li>
<li>Consolidate error mapping into named functions. ...</li>
<li>Factor repeated control-flow shapes into helpers. ...</li>
</ul>
</blockquote><p>...<br><a href="https://alexleighton.com/posts/2026-02-24-kbs-nesting-and-ideation.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>KBS: Adding Todos</title>
    <id>https://alexleighton.com/posts/2026-02-19-kbs-adding-todos.html</id>
    <link href="https://alexleighton.com/posts/2026-02-19-kbs-adding-todos.html" />
    <published>2026-02-20T03:45:00Z</published>
    <updated>2026-02-20T03:45:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>More guidance, and thoughts on automatic review.</p><p>Published on <span title="2026-02-20T03:45:00Z">2026-02-20</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>More guidance, and thoughts on automatic review.</h3><p>Published on <span title="2026-02-20T03:45:00Z">2026-02-20</span><br>Tags: article, code-review, llm, ocaml, software-eng, software-eng-auto</p><p>Today's changes build on the previous commit's unpacking of <code>Todo</code>, making a repository for <code>Todo</code>s, and hooking it up to the command line to provide <code>bs add todo ...</code>.</p>
<h2 id="more-guidance">More Guidance <a class="heading-anchor" href="#more-guidance" aria-hidden="true">#</a></h2>
<p>The implementation plan forgot to add a task for integration tests, which resulted in a <a href="https://github.com/alexleighton/knowledge-bases/blob/412117ae6940be69a6122a8a1d048f73ba0fa8de/docs/bin/principles.md?plain=1#L17-L40"><code>bin</code> principle</a> saying changes to <code>bin</code>, where we're only keeping command-line parsing and top-level orchestration, should always be accompanied by an integration test. I also had the agent put together an <a href="https://github.com/alexleighton/knowledge-bases/blob/412117ae6940be69a6122a8a1d048f73ba0fa8de/docs/test-integration/architecture.md">architecture document</a> guiding the structure of integration tests and their purpose.</p>
<p>In the hopes of keeping file sizes down, I added a line length principle to <a href="https://github.com/alexleighton/knowledge-bases/blob/412117ae6940be69a6122a8a1d048f73ba0fa8de/docs/bin/principles.md?plain=1#L52-L57"><code>bin</code></a> and <a href="https://github.com/alexleighton/knowledge-bases/blob/412117ae6940be69a6122a8a1d048f73ba0fa8de/docs/lib/principles.md?plain=1#L41-L46"><code>lib</code></a>. I'm not sure about these principles, because I think this can be automated as a lint. I will keep these in mind and improve the linting situation when I get another structural/syntactic principle that can be automated.</p><p>...<br><a href="https://alexleighton.com/posts/2026-02-19-kbs-adding-todos.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>KBS: Unpacking Todo</title>
    <id>https://alexleighton.com/posts/2026-02-18-kbs-unpacking-todo.html</id>
    <link href="https://alexleighton.com/posts/2026-02-18-kbs-unpacking-todo.html" />
    <published>2026-02-19T04:15:00Z</published>
    <updated>2026-02-19T04:15:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Guidance, prompts, and TDD.</p><p>Published on <span title="2026-02-19T04:15:00Z">2026-02-19</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Guidance, prompts, and TDD.</h3><p>Published on <span title="2026-02-19T04:15:00Z">2026-02-19</span><br>Tags: article, code-review, llm, ocaml, software-eng, software-eng-auto</p><p>Today's change to Knowledge Bases is addressing my unhappiness at the beginning of the month: <a href="../../../posts/2026-02-01-storage-for-knowledge-bases.html">"Data modeling"</a>. I've returned to the work of <a href="https://github.com/alexleighton/knowledge-bases/blob/d5fcf06481d710a6ecf35a0b874474e96d016dc5/lib/data/todo.ml#L19-L25">unpacking <code>Note</code> into <code>Todo</code></a>, to make it easier to control the TypeID prefix, and prepare for making a <code>Todo</code> repository.</p>
<h2 id="revisiting-guidance-docs">Revisiting Guidance Docs <a class="heading-anchor" href="#revisiting-guidance-docs" aria-hidden="true">#</a></h2>
<p>A handy thing about coding agents: they can make such a wide-ranging change with minimal effort (see the number of changed files in the commit). Additionally, the coding principles guidance doc paid for itself — agent code review noted the duplicated validation logic for title and content now in both <code>Note</code> and <code>Todo</code> and recommended deduplication. I redirected it to encapsulate the validation into new types, a favorite software engineering technique of mine (see <a href="../../../posts/2025-10-24-note-identifiers-and-tests.html">"Aside: Correct Construction"</a>), as well as <a href="https://github.com/alexleighton/knowledge-bases/blob/d5fcf06481d710a6ecf35a0b874474e96d016dc5/docs/lib/principles.md?plain=1#L15-L26">adjusted the guidance</a>.</p><p>...<br><a href="https://alexleighton.com/posts/2026-02-18-kbs-unpacking-todo.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>KBS: Going Automatic</title>
    <id>https://alexleighton.com/posts/2026-02-17-kbs-going-automatic.html</id>
    <link href="https://alexleighton.com/posts/2026-02-17-kbs-going-automatic.html" />
    <published>2026-02-18T05:30:00Z</published>
    <updated>2026-02-18T05:30:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Exploring a hands-off agent-driven workflow.</p><p>Published on <span title="2026-02-18T05:30:00Z">2026-02-18</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Exploring a hands-off agent-driven workflow.</h3><p>Published on <span title="2026-02-18T05:30:00Z">2026-02-18</span><br>Tags: article, git, llm, ocaml, software-eng, software-eng-auto</p><p>I've decided to use this project as a testbed for automatic development, to explore the kinds of techniques described in recent results [<a href="../../../posts/2026-02-12-new-software-engineering-modes.html">1</a>] [<a href="../../../posts/2026-02-14-more-software-engineering-research.html">2</a>], and to speed up development. I'll be making the vast majority of changes to the codebase via coding agents, exploring what controls are needed to stay hands-off yet maintain code quality.</p>
<h2 id="short-agentsmd">Short AGENTS.md <a class="heading-anchor" href="#short-agentsmd" aria-hidden="true">#</a></h2>
<p>I've played around with longer <code>AGENTS.md</code> and not seen much value. Agents would ignore pieces, and after a while it felt like simply a waste of context space. I'm keeping <a href="https://github.com/alexleighton/knowledge-bases/blob/223c4ece2550fb1196cec770d97762a66823fcda/AGENTS.md?plain=1"><code>AGENTS.md</code> short</a> this time around, "linking" to where more information can be retrieved, with the hope that brevity keeps the content important.</p><p>...<br><a href="https://alexleighton.com/posts/2026-02-17-kbs-going-automatic.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>Storage for Knowledge Bases</title>
    <id>https://alexleighton.com/posts/2026-02-01-storage-for-knowledge-bases.html</id>
    <link href="https://alexleighton.com/posts/2026-02-01-storage-for-knowledge-bases.html" />
    <published>2026-02-03T05:30:00Z</published>
    <updated>2026-02-03T05:30:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Progress, regrets, and motivation working with SQLite.</p><p>Published on <span title="2026-02-03T05:30:00Z">2026-02-03</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Progress, regrets, and motivation working with SQLite.</h3><p>Published on <span title="2026-02-03T05:30:00Z">2026-02-03</span><br>Tags: article, database, git, ocaml, software-eng</p><p>Today's update is brought to you by my lack of motivation 😆. I decided to work on building out a storage layer for Knowledge Bases objects, and then got burned by lack of interest.</p>
<p>Restating our storage requirements: we need notes, todos, etc to be persisted to disk in a way where git's diff will capably show edits, additions, and deletions, and which allows for easily querying these objects by identifier or by their relation to each other (we want to support parent-child, related-to, etc). To meet those requirements I intend to persist data in two ways. First, in an appropriately-sorted JSON or YAML file, to solve for the git-diff requirement. Second, in a <a href="https://en.wikipedia.org/wiki/SQLite">SQLite</a> database to make it easy to add/edit/delete/query. The program will mirror the SQLite database onto the JSON file unless it's determined to be out of date (i.e. when the git branch is changed), in which case we'll rebuild the database from the JSON file. For now I'm starting with the SQLite db, as that can be useful for purely local todo storage, and we'll bolt-on the ability to synchronize them over git later.</p><p>...<br><a href="https://alexleighton.com/posts/2026-02-01-storage-for-knowledge-bases.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>Year in Review: 2025</title>
    <id>https://alexleighton.com/posts/2025-12-31-year-in-review-2025.html</id>
    <link href="https://alexleighton.com/posts/2025-12-31-year-in-review-2025.html" />
    <published>2026-01-01T06:15:00Z</published>
    <updated>2026-01-01T06:15:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Building and blogging on this site the past year.</p><p>Published on <span title="2026-01-01T06:15:00Z">2026-01-01</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Building and blogging on this site the past year.</h3><p>Published on <span title="2026-01-01T06:15:00Z">2026-01-01</span><br>Tags: article, git, meta, ocaml, static-site</p><p>For 2024's New Year's resolution I decided I'd finally build and publish a personal website. That I'd write up my thoughts on the articles I was reading and post them instead of dumping them on friends 😆. In review, I'm happy to say I succeeded.</p>
<p>I wrote and published 164 blog posts over the past year, not counting this post. September was my most prolific month. I initialized the OCaml project that became the static site generator in late January, writing a <a href="../../../posts/2025-02-02-listening-to-the-resonance-between.html">test post</a> at the beginning of February to work out frontmatter parsing, markdown generation, and my custom templating language. The first "real" post came at the end of June, a few days after I bought the domain name.</p><p>...<br><a href="https://alexleighton.com/posts/2025-12-31-year-in-review-2025.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>Changelog: Individual Image Pages</title>
    <id>https://alexleighton.com/posts/2025-12-01-changelog-individual-image-pages.html</id>
    <link href="https://alexleighton.com/posts/2025-12-01-changelog-individual-image-pages.html" />
    <published>2025-12-02T05:45:00Z</published>
    <updated>2025-12-02T05:45:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Images now link to a page featuring the image.</p><p>Published on <span title="2025-12-02T05:45:00Z">2025-12-02</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Images now link to a page featuring the image.</h3><p>Published on <span title="2025-12-02T05:45:00Z">2025-12-02</span><br>Tags: changelog, ocaml, static-site</p><p>I've created individual image pages. Now all images will link to their own image page, which makes it easier to see the alt text and variant images. For example, <a href="../../../img/img_o_01k80xvwggfn1s20cv98d75bee.html">this page for the picture of my dog on Rialto Beach</a>.</p>
<p>Implementing this was pretty straightforward. My static site generator makes a distinction between "normal" pages, ones where the <code>Page</code> structure is constructible solely from the contents of a file (HTML or Markdown body with all the necessary metadata in a <a href="https://jekyllrb.com/docs/front-matter/">YAML frontmatter</a>), and "synthetic" pages, where the <code>Page</code> structure is constructed in OCaml code inside the generator. These image pages are synthetic: the generator constructs one for each image original used on the site, generated alongside other synthetic pages like the tag pages or blog post archive pages.</p><p>...<br><a href="https://alexleighton.com/posts/2025-12-01-changelog-individual-image-pages.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>TypeIDs for Knowledge Bases</title>
    <id>https://alexleighton.com/posts/2025-11-29-typeids-for-knowledge-bases.html</id>
    <link href="https://alexleighton.com/posts/2025-11-29-typeids-for-knowledge-bases.html" />
    <published>2025-11-30T04:00:00Z</published>
    <updated>2025-11-30T04:00:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Data consistency and the primary identifier for content.</p><p>Published on <span title="2025-11-30T04:00:00Z">2025-11-30</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Data consistency and the primary identifier for content.</h3><p>Published on <span title="2025-11-30T04:00:00Z">2025-11-30</span><br>Tags: article, git, ocaml, software-eng</p><h2 id="typeid">TypeID <a class="heading-anchor" href="#typeid" aria-hidden="true">#</a></h2>
<p><a href="https://github.com/jetify-com/typeid">TypeID</a> is a micro-standard combining a number of well-thought-out decisions.</p>
<blockquote>
<p>TypeIDs are a modern, type-safe extension of UUIDv7. Inspired by a similar use of prefixes in Stripe's APIs.</p>
<p>TypeIDs are canonically encoded as lowercase strings consisting of three parts:</p>
<ol>
<li>A type prefix (at most 63 characters in all lowercase snake_case ASCII <code>[a-z_]</code>).</li>
<li>An underscore '_' separator</li>
<li>A 128-bit UUIDv7 encoded as a 26-character string using a modified base32 encoding.</li>
</ol>
</blockquote>
<pre><code class="language-plain">user_2x4y6z8a0b1c2d3e4f5g6h7j8k
└──┘ └────────────────────────┘
type    uuid suffix (base32)
</code></pre>
<p>Most identifiers, barring a specialized context, should include a "type" for the identifier. Interconnected computer systems are always passing around identifiers — for someone holding an identifier in their database it is really valuable to know who that identifier belongs to as well as what the identifier is for. For example in a financial institution handling investments, <code>md_quote_2x4y6z8a0b1c2d3e4f5g6h7j8k</code> can tell the receiver that they're holding a reference to a stock price quote, owned by the market data (<code>md</code>) system. Identifier types give engineers metadata, which can be especially useful when there's a bug in the system — no need to stress whether the integer identifier you're currently holding came from the <code>user</code> table or <code>news</code> table.</p><p>...<br><a href="https://alexleighton.com/posts/2025-11-29-typeids-for-knowledge-bases.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>Notes and Todos</title>
    <id>https://alexleighton.com/posts/2025-10-31-notes-and-todos.html</id>
    <link href="https://alexleighton.com/posts/2025-10-31-notes-and-todos.html" />
    <published>2025-11-01T00:00:00Z</published>
    <updated>2025-11-01T00:00:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Small Knowledge Bases update.</p><p>Published on <span title="2025-11-01T00:00:00Z">2025-11-01</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Small Knowledge Bases update.</h3><p>Published on <span title="2025-11-01T00:00:00Z">2025-11-01</span><br>Tags: article, ocaml</p><p>Short update today — the definitions of <code>note</code> and <code>todo</code> types and their basic operations.</p>
<p>My intention is to represent arbitrary content as a <code>note</code>, and represent things to be done with <code>todo</code>. Notes are just an identifier, a title, and some content, while Todos also have a status and eventually relationships. I've left relationships between notes and todos for later. I'm not sure if I want them represented separately or as part of the structures.</p>
<h2 id="note">Note <a class="heading-anchor" href="#note" aria-hidden="true">#</a></h2>
<pre><code class="language-ocaml">(** Note type and operations.

    A note bundles an [Identifier.t] with a title and content.
*)

(** Abstract type of notes. *)
type t

(** [pp fmt t] prints the note [t] using the formatter [fmt]. *)
val pp : Format.formatter -&gt; t -&gt; unit

(** [show t] returns the same string that {!pp} would print. *)
val show : t -&gt; string

(** [make identifier title content] constructs a note after validating inputs.

    - [title] must be non-empty and at most 100 characters.
    - [content] must be non-empty and at most 10000 characters.

    @raise Invalid_argument if validation fails. *)
val make : Identifier.t -&gt; string -&gt; string -&gt; t

(** [id t] returns the note identifier. *)
val id : t -&gt; Identifier.t

(** [title t] returns the title of the note. *)
val title : t -&gt; string

(** [content t] returns the content of the note. *)
val content : t -&gt; string
</code></pre><p>...<br><a href="https://alexleighton.com/posts/2025-10-31-notes-and-todos.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>Note Identifiers and Tests</title>
    <id>https://alexleighton.com/posts/2025-10-24-note-identifiers-and-tests.html</id>
    <link href="https://alexleighton.com/posts/2025-10-24-note-identifiers-and-tests.html" />
    <published>2025-10-25T00:45:00Z</published>
    <updated>2025-10-25T00:45:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Extended discussion on data representation.</p><p>Published on <span title="2025-10-25T00:45:00Z">2025-10-25</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Extended discussion on data representation.</h3><p>Published on <span title="2025-10-25T00:45:00Z">2025-10-25</span><br>Tags: article, llm, ocaml</p><p>My inclination, after getting the hello world project set up in place, is to start defining the major data representations of the domain we're working in. Since I'm documenting the process, and I want to discuss decisions, I'm going to start out pretty slowly — today's update is basically just a type for representing identifiers for "notes" (whatever it is we decide to call the basic content building block).</p>
<h2 id="basic-definition-and-data-access">Basic Definition and Data Access <a class="heading-anchor" href="#basic-definition-and-data-access" aria-hidden="true">#</a></h2>
<p>The plan here is to expose an opaque type <code>t</code> for our identifier values (<code>Identifier.t</code>), and be explicit and deliberate about what data is exposed to the rest of the program. I liked how Beads stuck with an identifier format that's human readable, and likely prevalent in LLM training data, so we'll use the same format for our identifier: <code>[a-z]{1,5}-[0-9]+</code> (e.g. <code>kbase-12345</code>). Decomposing that identifier gets us a <code>namespace</code> and <code>raw_id</code>.</p><p>...<br><a href="https://alexleighton.com/posts/2025-10-24-note-identifiers-and-tests.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>Starting Knowledge Bases</title>
    <id>https://alexleighton.com/posts/2025-10-22-starting-knowledge-bases.html</id>
    <link href="https://alexleighton.com/posts/2025-10-22-starting-knowledge-bases.html" />
    <published>2025-10-22T13:30:00Z</published>
    <updated>2025-10-22T13:30:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Writing a coding agent tool in OCaml.</p><p>Published on <span title="2025-10-22T13:30:00Z">2025-10-22</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Writing a coding agent tool in OCaml.</h3><p>Published on <span title="2025-10-22T13:30:00Z">2025-10-22</span><br>Tags: article, git, llm, ocaml</p><p>A couple days ago, <a href="https://en.wikipedia.org/wiki/Steve_Yegge">Steve Yegge</a> <a href="https://steve-yegge.medium.com/beads-for-blobfish-80c7a2977ffa">wrote about a coding agent tool</a> he's been developing called <a href="https://github.com/steveyegge/beads">Beads</a>.</p>
<blockquote>
<p>Beads is a lightweight memory system for coding agents, using a graph-based issue tracker. Four kinds of dependencies work to chain your issues together like beads, making them easy for agents to follow for long distances, and reliably perform complex task streams in the right order.</p>
</blockquote>
<p>I haven't had an opportunity to see if it works well or not, but the feature set seems straightforward to implement and I've been looking for a project to work on which will involve OCaml and publishing to opam. I'll be documenting the development process here 😁.</p><p>...<br><a href="https://alexleighton.com/posts/2025-10-22-starting-knowledge-bases.html">Read the full post →</a></p>]]></content>
  </entry>
  
</feed>
