<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Posts Tagged "article" on Alex Leighton's Blog</title>
  <id>https://alexleighton.com/posts/tags/article-tag-feed.xml</id>
  <link href="https://alexleighton.com/posts/tags/article-tag-feed.xml" rel="self" />
  <link href="https://alexleighton.com/posts/tags/article.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>Convergent Tool Calling</title>
    <id>https://alexleighton.com/posts/2026-07-11-convergent-tool-calling.html</id>
    <link href="https://alexleighton.com/posts/2026-07-11-convergent-tool-calling.html" />
    <published>2026-07-11T19:30:00Z</published>
    <updated>2026-07-11T19:30:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Leaning on JavaScript for LLM-authored micro-programs.</p><p>Published on <span title="2026-07-11T19:30:00Z">2026-07-11</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Leaning on JavaScript for LLM-authored micro-programs.</h3><p>Published on <span title="2026-07-11T19:30:00Z">2026-07-11</span><br>Tags: article, commentary, javascript, kotlin, llm, software-eng, software-eng-auto</p><blockquote>
<p><a href="https://openai.com/index/gpt-5-6"><strong>OpenAI GPT-5.6 Announcement</strong> on 2026-07-09</a>:</p><p>GPT‑5.6 can write and run lightweight programs that coordinate tools, process intermediate results, monitor progress, and choose the next action as work unfolds. This lets tool-heavy tasks advance with fewer tokens, fewer model round trips, and less guidance. Instead of requiring developers to script every step or passing every tool response back through the model, <a href="https://developers.openai.com/api/docs/guides/tools-programmatic-tool-calling">Programmatic Tool Calling</a> in the Responses API can filter large amounts of intermediate data, retain only what matters, and adapt its workflow along the way.</p></blockquote>
<p>I am getting some sweet validation from this feature they revealed. I built a similar system at work recently, and I think it fits coding agent capabilities nicely.</p><p>...<br><a href="https://alexleighton.com/posts/2026-07-11-convergent-tool-calling.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>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>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>
  
  <entry>
    <title>Randomart</title>
    <id>https://alexleighton.com/posts/2025-09-05-randomart.html</id>
    <link href="https://alexleighton.com/posts/2025-09-05-randomart.html" />
    <published>2025-09-06T05:15:00Z</published>
    <updated>2025-09-06T05:15:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>SSH key visualization.</p><p>Published on <span title="2025-09-06T05:15:00Z">2025-09-06</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>SSH key visualization.</h3><p>Published on <span title="2025-09-06T05:15:00Z">2025-09-06</span><br>Tags: article, ocaml, security, software-eng</p><p>When using <a href="https://en.wikipedia.org/wiki/OpenSSH">OpenSSH</a>, you may see ASCII art from time to time, like:</p>
<pre><code>+--[ED25519 256]--+
|                 |
|     .           |
|      o          |
|     o o o  .    |
|     .B S oo     |
|     =+^ =...    |
|    oo#o@.o.     |
|    E+.&amp;.=o      |
|    ooo.X=.      |
+----[SHA256]-----+
</code></pre>
<p>This is output<sup><a href="#fn-1" id="ref-1-fn-1" role="doc-noteref" class="fn-label">[1]</a></sup> of the randomart SSH Key visualization algorithm, intended to leverage the user's visual pattern matching to quickly recognize (or not) a remote host key. It's known as the <a href="https://www.jfurness.uk/the-drunken-bishop-algorithm/">Drunken Bishop's algorithm</a>, contributed to the OpenSSH codebase by <a href="https://undeadly.org/cgi?action=article&amp;sid=20080615022750">Alexander von Gernler in 2008</a>.</p>
<blockquote>
<p><a href="https://marc.info/?l=openbsd-cvs&amp;m=121321826818823&amp;w=2"><strong>Alexander von Gernler</strong> on 2008-06-11</a>:</p><p>Introduc[ing] SSH Fingerprint ASCII Visualization, a technique inspired by the graphical hash visualization schemes known as "random art", and by Dan Kaminsky's musings on the subject during a BlackOp talk at the 23C3 in Berlin.</p>
<p>Scientific publication (original paper): "Hash Visualization: a New Technique to improve Real-World Security", Perrig A. and Song D., 1999, International Workshop on Cryptographic Techniques and E-Commerce (CrypTEC '99)</p></blockquote><p>...<br><a href="https://alexleighton.com/posts/2025-09-05-randomart.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>Building Search for this Site</title>
    <id>https://alexleighton.com/posts/2025-08-09-building-search-for-this-site.html</id>
    <link href="https://alexleighton.com/posts/2025-08-09-building-search-for-this-site.html" />
    <published>2025-08-10T05:10:37Z</published>
    <updated>2025-08-10T05:10:37Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>Search on a static site.</p><p>Published on <span title="2025-08-10T05:10:37Z">2025-08-10</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>Search on a static site.</h3><p>Published on <span title="2025-08-10T05:10:37Z">2025-08-10</span><br>Tags: article, java, javascript, ocaml, static-site</p><p>I like the idea of having search for my blog, to help readers find anything related to a specific topic, or something they'd read in the past. How do I make something as dynamic as search, but statically generated?</p>
<h2 id="requirements">Requirements <a class="heading-anchor" href="#requirements" aria-hidden="true">#</a></h2>
<ul>
<li><strong>The solution has to be small</strong> — part of my purpose in statically generating the site is to make it fast to download, fast to view. Whatever is chosen needs to maintain that vision.</li>
<li><strong>The solution can't introduce too many dependencies</strong> — I really like OCaml as a language, and I used building my personal site as an excuse to work in it. Introducing non-OCaml dependencies has to be done with care, as I don't want to maintain a Rube Goldberg machine for my website.</li>
</ul><p>...<br><a href="https://alexleighton.com/posts/2025-08-09-building-search-for-this-site.html">Read the full post →</a></p>]]></content>
  </entry>
  
  <entry>
    <title>Syntax Highlighting</title>
    <id>https://alexleighton.com/posts/2025-08-06-syntax-highlighting.html</id>
    <link href="https://alexleighton.com/posts/2025-08-06-syntax-highlighting.html" />
    <published>2025-08-06T13:30:00Z</published>
    <updated>2025-08-06T13:30:00Z</updated>
    <author><name>Alex Leighton</name></author>
    <summary type="html"><![CDATA[<p>We're not there yet.</p><p>Published on <span title="2025-08-06T13:30:00Z">2025-08-06</span></p>]]></summary>
    <content type="html"><![CDATA[<h3>We're not there yet.</h3><p>Published on <span title="2025-08-06T13:30:00Z">2025-08-06</span><br>Tags: article, css, html, javascript, static-site</p><p>Unfortunately I spoke too soon when I wrote the <a href="../../../posts/2025-07-18-building-this-site.html">Building this Site</a> post. I had a plan in place to use <a href="https://opam.ocaml.org/packages/hilite/"><code>hilite</code></a>, along with TextMate grammars and themes pulled from <a href="https://shiki.style/">Shiki</a> (which in turn gets its grammars and themes from TextMate/VSCode), but it fell through.</p>
<h2 id="the-plan">The Plan <a class="heading-anchor" href="#the-plan" aria-hidden="true">#</a></h2>
<p>The plan was to grab lightmode and darkmode themes from Shiki's <a href="https://github.com/shikijs/textmate-grammars-themes/tree/main/packages/tm-themes">trove of themes</a>, stripping out any extraneous styles (a lot of text-editor-specific colors are given) to arrive at two sets of colors for pre-determined class names. Shiki works similarly to <code>hilite</code>: during processing it tags portions of the source code with CSS tags derived from the grammars. All of the <a href="https://github.com/shikijs/textmate-grammars-themes/tree/main/packages/tm-grammars">TextMate grammars</a> employed by Shiki use a consistent naming scheme, ensuring themes always line up with the grammars, resulting in colored text. With a little adjustment of the theme CSS tags to account for any differences between Hilite's and Shiki's tagging, I'd have ahead-of-time syntax highlighting, with as many supported languages as VSCode.</p><p>...<br><a href="https://alexleighton.com/posts/2025-08-06-syntax-highlighting.html">Read the full post →</a></p>]]></content>
  </entry>
  
</feed>
