<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Alex Towell</title>
    <description>The latest articles on Forem by Alex Towell (@queelius).</description>
    <link>https://forem.com/queelius</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3561312%2F3bd5c5ef-734e-4811-af00-9df134329e1b.png</url>
      <title>Forem: Alex Towell</title>
      <link>https://forem.com/queelius</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/queelius"/>
    <language>en</language>
    <item>
      <title>Superintelligence May Not Require a Breakthrough</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Fri, 10 Apr 2026 02:14:55 +0000</pubDate>
      <link>https://forem.com/queelius/superintelligence-may-not-require-a-breakthrough-1ocg</link>
      <guid>https://forem.com/queelius/superintelligence-may-not-require-a-breakthrough-1ocg</guid>
      <description>&lt;p&gt;There is a version of the superintelligence story where a researcher has a conceptual breakthrough, some fundamental insight about cognition that nobody else has seen, and the world changes overnight. Good fiction. I've &lt;a href="https://metafunctor.com/writing/the-policy/" rel="noopener noreferrer"&gt;written some of it myself&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think the more plausible version is less cinematic. Superintelligence arrives through a sufficiently good build system. Better tooling. Longer optimization horizons. Richer scaffolding. The ingredients already exist. The recipe is engineering.&lt;/p&gt;

&lt;p&gt;I want to explain why I think this. The engineering argument is the scarier one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pretraining Lesson
&lt;/h2&gt;

&lt;p&gt;Start with what we know works. Large language models acquire broad capabilities during pretraining. Not because anyone designs those capabilities in. The data distribution is so massive and varied that the model is forced to compress deeper regularities rather than memorize surface patterns. You train it to predict the next token, and what falls out looks like understanding.&lt;/p&gt;

&lt;p&gt;The model didn't learn task-specific scripts. It learned representations general enough to transfer across tasks it never saw.&lt;/p&gt;

&lt;p&gt;Now consider what happens when you apply reinforcement learning over long-horizon tasks. Not single-step rewards. Optimization over extended sequences: searching, backtracking, verifying, decomposing problems, maintaining state across hundreds of steps. If the task distribution is rich enough, the model can't get by with shallow heuristics. It has to learn something that works like planning.&lt;/p&gt;

&lt;p&gt;I traced this progression &lt;a href="https://metafunctor.com/post/2026-01-rational-agents-llms/" rel="noopener noreferrer"&gt;in an earlier post&lt;/a&gt;: the history of AI is really about finding representations that make decision-making tractable. Search gave way to heuristics, heuristics to learned value functions, value functions to pretrained priors over rational behavior. Each step made the representation richer.&lt;/p&gt;

&lt;p&gt;The next step is not a new architecture. It is optimization over longer trajectories. First the model fumbles through specific tasks. Then it compresses the deeper regularity, the same way pretraining compresses language. Planning, self-correction, tool use, state management: not separate faculties waiting to be discovered. They are what falls out when you optimize over long enough horizons.&lt;/p&gt;

&lt;p&gt;Reasoning is not a magic ingredient. It is a policy learned over long trajectories.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually See
&lt;/h2&gt;

&lt;p&gt;That is the theoretical argument. Here is the empirical one.&lt;/p&gt;

&lt;p&gt;I spend most of my working hours inside Claude Code. Opus 4.6, million-token context. It decomposes tasks, dispatches subagents, verifies its own work, maintains state across hundreds of tool calls. It does this not because the base model acquired some new cognitive faculty since the last release. It does this because scaffolding gives it the ecology to express capabilities that were already there in proto-form.&lt;/p&gt;

&lt;p&gt;Tool use lets it act on the world. Persistent memory lets it hold context across sessions. Task decomposition lets it manage complexity. Self-verification lets it catch its own mistakes. A million tokens lets it hold an entire project in working memory. None of these are architectural breakthroughs. They are environment design.&lt;/p&gt;

&lt;p&gt;Same pattern everywhere. AlphaProof's mathematical reasoning came from tool-augmented search, not a bigger model. Code interpreters let models verify their own outputs by running them. Agent frameworks compose simple capabilities into complex behaviors. The jump came from building a richer environment, not from changing the engine.&lt;/p&gt;

&lt;p&gt;And the effects compound. Each tool makes every other tool more useful. A model with memory and tool use is qualitatively different from one with just tool use. Add self-verification and it changes again. This is not linear improvement. Network effects applied to cognition.&lt;/p&gt;

&lt;p&gt;The model is the engine. The ecosystem is the vehicle. Evolution did not produce mathematicians by handing plankton a theorem prover and saying "best of luck." It built an ecology. We are doing something similar, less gracefully, with scaffolding and RL and tool chains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats That Matter
&lt;/h2&gt;

&lt;p&gt;I should be honest about what this doesn't guarantee.&lt;/p&gt;

&lt;p&gt;Long-horizon RL does not automatically produce clean reasoning. It produces whatever policy scores well. That includes looking thoughtful, exploiting loopholes, overfitting to scaffolds, and learning shallow heuristics that mimic planning until the distribution shifts and the whole thing collapses. Reward hacking is the central failure mode. It gets harder to detect as the horizon lengthens. A model that appears to reason carefully over a thousand steps may be doing something much more superficial.&lt;/p&gt;

&lt;p&gt;Credit assignment is brutal over long horizons. The reward signal dilutes across hundreds of steps. The model has to discover useful intermediate behaviors before it can be rewarded for them. This is why curriculum design, verifiable subgoals, and tool-mediated feedback matter. You can't just hand a model a hard problem and a reward signal and expect convergence. The training ecology matters as much as the objective.&lt;/p&gt;

&lt;p&gt;None of this is certain. The claim is not "we have the recipe." The claim is "we may already have the ingredients, and the recipe looks more like engineering than like physics."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Phase Change
&lt;/h2&gt;

&lt;p&gt;If the ingredients are already here, the transition doesn't look like a dramatic announcement. It looks incremental, and then it doesn't.&lt;/p&gt;

&lt;p&gt;For a while, progress looks like tooling improvements. Bigger context windows. Better tool integration. Smarter memory. More capable agent loops. Each one feels like a minor version bump. The benchmarks tick up.&lt;/p&gt;

&lt;p&gt;Then at some point the policy has absorbed enough structure that it generalizes across cognitive tasks the way pretrained models generalize across language. Not domain-specific planning, but portable cognitive strategy: maintain state, decompose problems, search selectively, verify work, recover from dead ends. At that point the curve changes.&lt;/p&gt;

&lt;p&gt;The possibility that unsettles me is not that superintelligence requires some deep theoretical insight we haven't found. It's that it doesn't. That it's blocked on engineering, scale, reward design, and the stubborn patience to optimize over longer and longer horizons. That the distance between here and there is measured in build quality, not in breakthroughs.&lt;/p&gt;

&lt;p&gt;That would be a strange day. And it might not announce itself.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>reasoning</category>
      <category>reinforcementlearning</category>
      <category>superintelligence</category>
    </item>
    <item>
      <title>dapple: Terminal Graphics, Composed</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Fri, 10 Apr 2026 02:14:54 +0000</pubDate>
      <link>https://forem.com/queelius/dapple-terminal-graphics-composed-4ba2</link>
      <guid>https://forem.com/queelius/dapple-terminal-graphics-composed-4ba2</guid>
      <description>&lt;p&gt;I live in the terminal. Most of my tools are CLIs. When I want to see something visual (an image, a plot, a table of results), I do not want to leave the terminal to see it.&lt;/p&gt;

&lt;p&gt;Terminal graphics tools exist, but they are fragmented. One library does braille characters. Another does quadrant blocks. A third handles sixel. Each has its own API, its own conventions, its own way of thinking about the same problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/queelius/dapple" rel="noopener noreferrer"&gt;dapple&lt;/a&gt; unifies them. One Canvas class, seven pluggable renderers, and eleven CLI tools built on top. The core depends only on numpy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;The insight is that "render a bitmap to the terminal" is a single problem with multiple encodings. Braille characters pack 2x4 dots per cell. Quadrant blocks give you 2x2 with color. Sextants give 2x3. Sixel and kitty give true pixels if your terminal supports them. These are all the same operation: map a grid of values to a grid of characters.&lt;/p&gt;

&lt;p&gt;So dapple makes the renderer a parameter, not an architecture decision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dapple&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;braille&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quadrants&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sextants&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dapple.adapters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;from_pil&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;PIL&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;

&lt;span class="n"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;from_pil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;photo.jpg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;out&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;braille&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# Unicode braille (2x4 dots per cell)
&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;out&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quadrants&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# block characters with ANSI color
&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;out&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sextants&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# higher vertical resolution
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Load once, render anywhere. The renderers are frozen dataclasses. &lt;code&gt;braille(threshold=0.3)&lt;/code&gt; returns a new renderer with different settings; nothing mutates. They write directly to a &lt;code&gt;TextIO&lt;/code&gt; stream, never building the full output as an intermediate string.&lt;/p&gt;

&lt;h2&gt;
  
  
  Renderers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Renderer&lt;/th&gt;
&lt;th&gt;Cell Size&lt;/th&gt;
&lt;th&gt;Colors&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;braille&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2x4&lt;/td&gt;
&lt;td&gt;mono/gray/true&lt;/td&gt;
&lt;td&gt;Structure, edges, piping, accessibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quadrants&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2x2&lt;/td&gt;
&lt;td&gt;ANSI 256/true&lt;/td&gt;
&lt;td&gt;Photos, balanced resolution and color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sextants&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2x3&lt;/td&gt;
&lt;td&gt;ANSI 256/true&lt;/td&gt;
&lt;td&gt;Higher vertical resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ascii&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1x2&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Universal compatibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sixel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1x1&lt;/td&gt;
&lt;td&gt;palette&lt;/td&gt;
&lt;td&gt;True pixels (xterm, mlterm, foot)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kitty&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1x1&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;True pixels (kitty, wezterm)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fingerprint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;8x16&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Artistic glyph matching&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In practice I use braille and sextants. They work everywhere. Kitty protocol broke things completely inside Claude Code (a TUI), and I have not tested sixel enough to trust it. Braille and sextants are the universal goto.&lt;/p&gt;

&lt;p&gt;One honest limitation: Claude Code hides terminal output behind a Ctrl-O expand, so my carefully rendered graphics end up collapsed by default. I think recent hooks or tool-result handling might fix this, but I have not confirmed it yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Layers
&lt;/h2&gt;

&lt;p&gt;The architecture has strict boundaries:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Core&lt;/strong&gt; (numpy only): Canvas, renderers, color handling, preprocessing, layout primitives (Frame, Grid).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adapters&lt;/strong&gt; (optional deps): Bridge PIL, matplotlib, cairo, and ANSI art to Canvas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extras&lt;/strong&gt; (optional deps): The CLI tools. Each one is a separate install group.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;dapple                 &lt;span class="c"&gt;# core only&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;dapple[imgcat]         &lt;span class="c"&gt;# image viewer&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;dapple[all-tools]      &lt;span class="c"&gt;# everything&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Core never imports PIL. Adapters never import extras. This matters because the core is tiny and fast, and the CLI tools pull in their own dependencies without bloating each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CLI Tools
&lt;/h2&gt;

&lt;p&gt;I built eleven tools, each owning a domain rather than a file format. "Show me this data" should not require knowing whether the file is JSON or CSV. "Display these images" should not require a different tool for one image versus twelve.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;imgcat&lt;/td&gt;
&lt;td&gt;Images (single + grid)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;datcat&lt;/td&gt;
&lt;td&gt;Structured data (JSON/JSONL/CSV/TSV)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vidcat&lt;/td&gt;
&lt;td&gt;Video (stacked frames, playback, asciinema export)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mdcat&lt;/td&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;htmlcat&lt;/td&gt;
&lt;td&gt;HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pdfcat&lt;/td&gt;
&lt;td&gt;PDFs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;funcat&lt;/td&gt;
&lt;td&gt;Math and parametric plots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansicat&lt;/td&gt;
&lt;td&gt;ANSI art&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;compcat&lt;/td&gt;
&lt;td&gt;Renderer comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;plotcat&lt;/td&gt;
&lt;td&gt;Faceted data plots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dashcat&lt;/td&gt;
&lt;td&gt;YAML-driven dashboards&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few worth explaining:&lt;/p&gt;

&lt;h3&gt;
  
  
  datcat
&lt;/h3&gt;

&lt;p&gt;datcat handles JSON, JSONL, CSV, and TSV. Format detection is automatic. Internally everything becomes &lt;code&gt;list[dict]&lt;/code&gt;. CSV rows become dicts on parse. One representation means the downstream code (table formatting, chart extraction, plotting) does not branch on input format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;datcat records.json              &lt;span class="c"&gt;# JSON table&lt;/span&gt;
datcat events.jsonl &lt;span class="nt"&gt;--bar&lt;/span&gt; event  &lt;span class="c"&gt;# JSONL bar chart&lt;/span&gt;
datcat weather.csv &lt;span class="nt"&gt;--sort&lt;/span&gt; temp_c &lt;span class="c"&gt;# CSV sorted by column&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  imgcat
&lt;/h3&gt;

&lt;p&gt;When imgcat receives multiple images, it switches to grid mode automatically. The layout uses dapple's Frame and Grid primitives, the same ones compcat and dashcat use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;imgcat photo.jpg                     &lt;span class="c"&gt;# single image&lt;/span&gt;
imgcat photos/&lt;span class="k"&gt;*&lt;/span&gt;.jpg &lt;span class="nt"&gt;--cols&lt;/span&gt; 3         &lt;span class="c"&gt;# 3-column contact sheet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Preprocessing flags (&lt;code&gt;--contrast&lt;/code&gt;, &lt;code&gt;--dither&lt;/code&gt;, &lt;code&gt;--invert&lt;/code&gt;) apply to every image in the grid.&lt;/p&gt;

&lt;h3&gt;
  
  
  vidcat
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--play&lt;/code&gt; flag renders frames in-place using ANSI cursor movement. Instead of printing each frame below the last (which scrolls your terminal into oblivion), it overwrites the previous frame.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vidcat video.mp4 &lt;span class="nt"&gt;--play&lt;/span&gt;              &lt;span class="c"&gt;# 10 fps default&lt;/span&gt;
vidcat video.mp4 &lt;span class="nt"&gt;--play&lt;/span&gt; &lt;span class="nt"&gt;--fps&lt;/span&gt; 24     &lt;span class="c"&gt;# faster&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mechanism: render the first frame, count its output lines, then for each subsequent frame write &lt;code&gt;\033[{N}A\033[J&lt;/code&gt; (cursor up N, clear to end) before rendering. Falls back to stacked output if stdout is not a TTY.&lt;/p&gt;

&lt;h3&gt;
  
  
  htmlcat
&lt;/h3&gt;

&lt;p&gt;Converts HTML to markdown via &lt;a href="https://github.com/matthewwithanm/python-markdownify" rel="noopener noreferrer"&gt;markdownify&lt;/a&gt;, then renders through Rich using the same pipeline as mdcat. Good for documentation and articles. Not designed for CSS-heavy web apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layout and Charts
&lt;/h2&gt;

&lt;p&gt;Frame and Grid are layout primitives. Frame adds borders and titles around a canvas. Grid arranges canvases in rows and columns. These compose: a Grid of Framed canvases, a Frame around a Grid. dashcat uses this to build terminal dashboards from YAML config.&lt;/p&gt;

&lt;p&gt;Two chart APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bitmap charts&lt;/strong&gt; (&lt;code&gt;dapple.charts&lt;/code&gt;): sparklines, line plots, bar charts, histograms, heatmaps. These return Canvas objects, composable with everything else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text charts&lt;/strong&gt; (&lt;code&gt;dapple.textchart&lt;/code&gt;): text-mode bar charts and sparklines, returning ANSI strings. Used by datcat for quick inline visualization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Status
&lt;/h2&gt;

&lt;p&gt;dapple is on &lt;a href="https://pypi.org/project/dapple/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt;. Docs at &lt;a href="https://queelius.github.io/dapple/" rel="noopener noreferrer"&gt;queelius.github.io/dapple&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The core and the CLI tools are stable. I use them daily. I have been experimenting with sextants (the 2x3 block characters give surprisingly good results) and tried generalizing the fingerprint renderer to match over a much larger set of Unicode glyphs. That did not work very well. The architecture is settled and the tools work.&lt;/p&gt;

</description>
      <category>dapple</category>
      <category>terminalgraphics</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Posthumous: A Federated Dead Man's Switch</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Fri, 10 Apr 2026 02:14:21 +0000</pubDate>
      <link>https://forem.com/queelius/posthumous-a-federated-dead-mans-switch-2ii8</link>
      <guid>https://forem.com/queelius/posthumous-a-federated-dead-mans-switch-2ii8</guid>
      <description>&lt;p&gt;Some things should only happen after you can't do them yourself.&lt;/p&gt;

&lt;p&gt;Posthumous is a self-hosted dead man's switch. You check in periodically (via phone, browser, CLI, or API call) and if you stop, it progresses through escalating stages before triggering automated actions: sending notifications, running scripts, whatever you've configured.&lt;/p&gt;

&lt;p&gt;I built it because the existing options are either cloud-hosted (you're trusting someone else's uptime for your most important automation) or single-node (one server failure and silence is indistinguishable from death). Posthumous is federated, multiple nodes watch each other, and fully self-hosted.&lt;/p&gt;

&lt;p&gt;This post walks through the basic workflows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Installation is a single pip command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;posthumous
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialization generates a TOTP secret and creates the config directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;phm init &lt;span class="nt"&gt;--node-name&lt;/span&gt; cerebro
Generated new TOTP secret.
Config created at ~/.posthumous/config.yaml
Example script created at ~/.posthumous/scripts/example.sh

&lt;span class="o"&gt;==================================================&lt;/span&gt;
TOTP Setup - Scan with your authenticator app:
&lt;span class="o"&gt;==================================================&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;QR code appears here]

Manual entry URI: otpauth://totp/Posthumous:cerebro?secret&lt;span class="o"&gt;=&lt;/span&gt;...&amp;amp;issuer&lt;span class="o"&gt;=&lt;/span&gt;Posthumous
Secret: JBSWY3DPEHPK3PXP

&lt;span class="o"&gt;==================================================&lt;/span&gt;
IMPORTANT: Save this secret securely!
&lt;span class="o"&gt;==================================================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You scan the QR code with any authenticator app (Google Authenticator, Authy, 1Password, whatever generates TOTP codes). That code is how you prove you're alive.&lt;/p&gt;

&lt;p&gt;The config file (&lt;code&gt;~/.posthumous/config.yaml&lt;/code&gt;) controls timing, notifications, and actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;node_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cerebro&lt;/span&gt;
&lt;span class="na"&gt;secret_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JBSWY3DPEHPK3PXP&lt;/span&gt;
&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0:8420"&lt;/span&gt;
&lt;span class="na"&gt;base_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://posthumous.example.com:8420"&lt;/span&gt;
&lt;span class="na"&gt;checkin_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;7 days&lt;/span&gt;
&lt;span class="na"&gt;warning_start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;8 days&lt;/span&gt;
&lt;span class="na"&gt;grace_start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12 days&lt;/span&gt;
&lt;span class="na"&gt;trigger_at&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;14 days&lt;/span&gt;
&lt;span class="na"&gt;notifications&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ntfy://my-posthumous-channel&lt;/span&gt;
&lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;on_warning&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;notify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check-in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;needed.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{days_left}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;days&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;remaining.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Check&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{checkin_url}"&lt;/span&gt;
  &lt;span class="na"&gt;on_grace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;notify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;URGENT:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Posthumous&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;triggers&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{hours_left}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hours.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Check&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{checkin_url}"&lt;/span&gt;
  &lt;span class="na"&gt;on_trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;notify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Posthumous&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;has&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;activated.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Dashboard:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{dashboard_url}"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scripts/release-credentials.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notification messages support template variables like &lt;code&gt;{checkin_url}&lt;/code&gt; and &lt;code&gt;{dashboard_url}&lt;/code&gt;, so push notifications to your phone include a direct link back to the check-in page.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Check-in Flow
&lt;/h2&gt;

&lt;p&gt;Start the daemon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;phm run
Starting Posthumous node &lt;span class="s1"&gt;'cerebro'&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This launches the web server, watchdog timer, and scheduler. Navigate to the check-in page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmetafunctor.com%2F01-checkin-armed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmetafunctor.com%2F01-checkin-armed.png" alt="The check-in page in ARMED state, showing a TOTP input field and the node name" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The UI is intentionally minimal. Dark theme, big input, works on a phone screen. Enter your 6-digit TOTP code and hit Check In.&lt;/p&gt;

&lt;p&gt;After a successful check-in, the timer resets and you see the countdown:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmetafunctor.com%2F03-checkin-after.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmetafunctor.com%2F03-checkin-after.png" alt="Check-in page after successful authentication, showing time since last check-in and time until trigger" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The status line tells you exactly how long until the system would escalate.&lt;/p&gt;

&lt;p&gt;You can also check in via the CLI or the JSON API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CLI check-in (prompts for TOTP)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;phm checkin
TOTP code: 645557
Check-in accepted
  Status: ARMED
  Next deadline: 2026-02-28 11:41 UTC

&lt;span class="c"&gt;# API check-in (for automation)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8420/checkin &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"totp": "645557"}'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;, &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"armed"&lt;/span&gt;, &lt;span class="s2"&gt;"next_deadline"&lt;/span&gt;: &lt;span class="s2"&gt;"2026-02-28T11:41:43+00:00"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Dashboard
&lt;/h2&gt;

&lt;p&gt;The dashboard shows everything at a glance: countdown timers, check-in history, peer status, and scheduled post-trigger actions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmetafunctor.com%2F04-dashboard-armed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmetafunctor.com%2F04-dashboard-armed.png" alt="Dashboard in ARMED state showing time remaining until each escalation stage, check-in history, peer status, and scheduled items" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The color coding matches the urgency: green for "Until warning" (you're fine), orange for "Until grace" (getting close), red for "Until trigger" (last chance). Auto-refreshes every 60 seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  The State Machine
&lt;/h2&gt;

&lt;p&gt;Posthumous progresses through four states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ARMED ──timeout──&amp;gt; WARNING ──timeout──&amp;gt; GRACE ──timeout──&amp;gt; TRIGGERED
  ^                   |                   |                    |
  └─── check-in ──────┴─── check-in ─────┘                    v
                                                    (scheduler runs forever)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A check-in from any pre-trigger state resets to ARMED. &lt;strong&gt;TRIGGERED is terminal.&lt;/strong&gt; Once activated, no check-in can undo it. This is by design: if the switch has fired, you want the actions to complete.&lt;/p&gt;

&lt;p&gt;Each transition fires its configured actions. If the node was offline and missed intermediate states (say it was down during WARNING and comes back during GRACE), the watchdog fires all skipped callbacks in order before reaching the current state. No notifications are silently dropped.&lt;/p&gt;

&lt;p&gt;When the system reaches TRIGGERED, the check-in page locks out:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmetafunctor.com%2F06-checkin-triggered.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmetafunctor.com%2F06-checkin-triggered.png" alt="Check-in page in TRIGGERED state, the TOTP form is replaced with a message explaining the node is triggered" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the dashboard reflects the terminal state:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmetafunctor.com%2F05-dashboard-triggered.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmetafunctor.com%2F05-dashboard-triggered.png" alt="Dashboard in TRIGGERED state showing all stages as Active/Activated with the trigger timestamp" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Notifications
&lt;/h2&gt;

&lt;p&gt;Posthumous uses &lt;a href="https://github.com/caronc/apprise" rel="noopener noreferrer"&gt;Apprise&lt;/a&gt; for notifications, which means it supports 100+ notification services out of the box: ntfy, Pushover, Telegram, Discord, email, Slack, and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;notifications&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ntfy://my-posthumous-channel&lt;/span&gt;
  &lt;span class="na"&gt;urgent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pover://user@token&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tgram://bot_token/chat_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each escalation stage can target different channels with different messages. Warning might go to ntfy (a gentle ping), while grace and trigger go to Pushover and Telegram (hard to miss).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;{checkin_url}&lt;/code&gt; variable means warning notifications include a clickable link directly to the check-in page. Open the notification on your phone, tap the link, enter the TOTP code. Three taps and you're checked in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Federation
&lt;/h2&gt;

&lt;p&gt;A single node has a single point of failure. If the server goes down, silence looks the same as death, which means either false triggers or missed real triggers.&lt;/p&gt;

&lt;p&gt;Posthumous solves this with federation. Multiple nodes share the same TOTP secret and communicate via HMAC-signed HTTP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Node A's config&lt;/span&gt;
&lt;span class="na"&gt;peers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://node-b.example.com:8420&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://node-c.example.com:8420&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you check in to any node, it broadcasts to all peers. When any node triggers, it broadcasts that too. The design bias is deliberate: &lt;strong&gt;duplicates over silence&lt;/strong&gt;. Multiple nodes may fire the same notification (annoying but survivable). A missed trigger is not.&lt;/p&gt;

&lt;p&gt;Each node tracks peer health independently, and the dashboard shows peer status with connection age and failure counts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Post-Trigger Scheduling
&lt;/h2&gt;

&lt;p&gt;Once triggered, Posthumous doesn't just fire-and-forget. A scheduler runs indefinitely, executing actions on configurable schedules using a small DSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;post_trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;weekly-reminder&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;every week after trigger&lt;/span&gt;
    &lt;span class="na"&gt;notify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Posthumous&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;was&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;triggered&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{days_left}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;days&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ago."&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;credential-release&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30 days after trigger&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scripts/release-credentials.sh&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;annual-memorial&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;every year on trigger anniversary&lt;/span&gt;
    &lt;span class="na"&gt;notify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Annual&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;memorial&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;notification."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;when&lt;/code&gt; expressions support relative timing (&lt;code&gt;30 days after trigger&lt;/code&gt;), recurring patterns (&lt;code&gt;every week after trigger&lt;/code&gt;), anniversaries (&lt;code&gt;every year on trigger anniversary&lt;/code&gt;), and absolute dates. Each execution is deduplicated by period key. If a node restarts, it won't re-run actions for the current period.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;Authentication uses TOTP (the same protocol as Google Authenticator). This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No passwords stored on the server&lt;/strong&gt;, only the shared secret&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-based codes expire every 30 seconds&lt;/strong&gt;, replay attacks have a narrow window&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brute-force protection&lt;/strong&gt;, after configurable failed attempts the account locks out for a configurable duration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The check-in page locks out after too many failures, and the API returns HTTP 429.&lt;/p&gt;

&lt;p&gt;Peer communication is authenticated with HMAC-SHA256 signatures derived from the shared secret. State files can optionally be encrypted at rest with Fernet (AES-128-CBC), enabled with a single config flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;encrypt_at_rest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Status at a Glance
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;phm status
Node: cerebro
Status: ARMED
Last check-in: 2026-02-14 11:41 UTC

Warning &lt;span class="k"&gt;in&lt;/span&gt;:  6d 23h
Grace &lt;span class="k"&gt;in&lt;/span&gt;:    10d 23h
Trigger &lt;span class="k"&gt;in&lt;/span&gt;:  12d 23h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or hit the JSON API for monitoring integrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:8420/status | python &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"node_name"&lt;/span&gt;: &lt;span class="s2"&gt;"cerebro"&lt;/span&gt;,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"armed"&lt;/span&gt;,
    &lt;span class="s2"&gt;"last_checkin"&lt;/span&gt;: &lt;span class="s2"&gt;"2026-02-14T11:41:43+00:00"&lt;/span&gt;,
    &lt;span class="s2"&gt;"time_remaining"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"until_warning"&lt;/span&gt;: 596503.0,
        &lt;span class="s2"&gt;"until_grace"&lt;/span&gt;: 942103.0,
        &lt;span class="s2"&gt;"until_trigger"&lt;/span&gt;: 1114903.0
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Posthumous is at v0.5. The core workflows are solid: check-in, state machine, notifications, federation, scheduling, encryption at rest. Some things I'm considering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Static site integration&lt;/strong&gt;: generating a Hugo/Jekyll site from post-trigger content, hosted on GitHub Pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-factor escalation&lt;/strong&gt;: requiring check-ins from multiple sources (web + CLI + API) before considering you "alive"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better peer discovery&lt;/strong&gt;: automatic peer registration instead of manual URL configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code is on GitHub: &lt;a href="https://github.com/queelius/posthumous" rel="noopener noreferrer"&gt;queelius/posthumous&lt;/a&gt;. It's a single &lt;code&gt;pip install&lt;/code&gt; and about 2,200 lines of Python (plus 3,700 lines of tests at 99% coverage).&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Posthumous is named for the obvious reason. Some automations only make sense after the fact.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>posthumous</category>
      <category>deadmansswitch</category>
      <category>python</category>
      <category>asyncio</category>
    </item>
    <item>
      <title>Intelligence is a Shape, Not a Scalar</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Fri, 10 Apr 2026 02:14:05 +0000</pubDate>
      <link>https://forem.com/queelius/intelligence-is-a-shape-not-a-scalar-35am</link>
      <guid>https://forem.com/queelius/intelligence-is-a-shape-not-a-scalar-35am</guid>
      <description>&lt;h1&gt;
  
  
  Intelligence is a Shape, Not a Scalar
&lt;/h1&gt;

&lt;p&gt;François Chollet posted something recently that I keep thinking about. It sounds reasonable and is mostly wrong:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One of the biggest misconceptions people have about intelligence is seeing it as some kind of unbounded scalar stat, like height. "Future AI will have 10,000 IQ", that sort of thing. Intelligence is a conversion ratio, with an optimality bound. Increasing intelligence is not so much like "making the tower taller", it's more like "making the ball rounder". At some point it's already pretty damn spherical and any improvement is marginal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He's right about the scalar part. Intelligence is not height. "10,000 IQ" is meaningless. He's right that there are diminishing returns near an optimum. He's right that speed, memory, and recall are separate from the core conversion ratio.&lt;/p&gt;

&lt;p&gt;Where he's wrong is the ball.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Claim
&lt;/h2&gt;

&lt;p&gt;Chollet defines intelligence as the efficiency with which a system converts experience into generalizable models. Sample efficiency. How little data do you need to see before you can handle novel situations? This is a clean definition. It has a theoretical optimum (Solomonoff induction), and Chollet's claim is that human intelligence is already close to that optimum. The ball is already pretty round.&lt;/p&gt;

&lt;p&gt;The supporting evidence is real. Humans score ~85% on ARC (the Abstraction and Reasoning Corpus, which Chollet designed to measure exactly this). Current AI systems, with vastly more data and compute, score significantly lower. Human sample efficiency on fluid reasoning tasks is genuinely impressive. We generalize from very few examples. We transfer knowledge across domains. We build theoretical models that predict situations we have never encountered.&lt;/p&gt;

&lt;p&gt;Chollet also argues that the advantages machines will have (processing speed, unlimited working memory, perfect recall) are "mostly things humans can also access through externalized cognitive tools." Calculators, databases, notebooks. The scaffolding can be externalized. The core intelligence is already near-optimal.&lt;/p&gt;

&lt;p&gt;This is a good argument. I think it's wrong in three ways, and the third way is the one that worries me.&lt;/p&gt;

&lt;h2&gt;
  
  
  No Free Lunch
&lt;/h2&gt;

&lt;p&gt;The No Free Lunch theorem says: there is no algorithm that is optimal across all possible problems. Any algorithm that performs well on one class of problems performs poorly on another class. Optimality is always relative to a distribution.&lt;/p&gt;

&lt;p&gt;The human cognitive architecture has a specific inductive bias. The 7+-2 working memory constraint forces compression: you can only hold a few items in conscious consideration at once, so information must be compressed (simplified, abstracted, modeled) to pass through. This compression is not a bug. It is the mechanism that produces abstraction, generalization, and theoretical reasoning. The bottleneck IS the source of human-type intelligence.&lt;/p&gt;

&lt;p&gt;But the bottleneck is not a universal compression optimum. It is the specific compression regime that was selected for by the distribution of problems ancestral humans faced: tracking social dynamics (~7 agents), composing tool-use sequences (~7 steps), navigating spatial environments (~7 landmarks). These problems have a specific structure: moderate dimensionality, hierarchically decomposable, amenable to lossy compression into simple models.&lt;/p&gt;

&lt;p&gt;Chollet's ball is round in the dimensions evolution tested. NFL guarantees it is flat in dimensions evolution did not test. The optimality bound he identifies is real, but it is niche-specific. The 7+-2 bias is an excellent fit for problems of moderate, decomposable complexity. It is a poor fit for problems whose essential structure lives in high-dimensional joint distributions that cannot be decomposed into 7-variable chunks without losing the signal.&lt;/p&gt;

&lt;p&gt;These problems exist. We hit them regularly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working Memory is Composition, Not Storage
&lt;/h2&gt;

&lt;p&gt;Chollet says machines' memory advantages are "mostly things humans can also access through externalized cognitive tools." This is the weakest point in his argument.&lt;/p&gt;

&lt;p&gt;A notebook gives you external storage. A database gives you perfect recall. But neither gives you what the working memory bottleneck actually constrains: simultaneous composition. The bottleneck is not a storage limit. It is a limit on how many items you can hold in active consideration at the same time, relating them to each other, perceiving patterns across them.&lt;/p&gt;

&lt;p&gt;Writing things down does not fix this. You can write 500 variables in a notebook. You can retrieve any of them on demand. But you still have to reason about their relationships through the bottleneck, 7 at a time, serially. The patterns that exist in the 500-variable joint distribution but not in any 7-variable marginal are invisible to you, even with perfect external storage.&lt;/p&gt;

&lt;p&gt;AlphaFold is the concrete example. Protein folding is a problem whose answer lives in dimensions we cannot fit through working memory. The 3D structure of a protein is determined by the simultaneous interaction of thousands of residues, each one influencing the others in ways that depend on the configuration of all the rest. The essential structure is in the joint distribution. It cannot be decomposed into 7-variable chunks and recombined, because the interactions are non-linear and non-decomposable.&lt;/p&gt;

&lt;p&gt;Humans tried to solve protein folding for decades. We had external tools. We had supercomputers. We had the full apparatus of molecular biology and physical chemistry. We could not solve it, because the problem's structure does not fit through our bottleneck.&lt;/p&gt;

&lt;p&gt;AlphaFold solved it by operating at a compositional depth humans cannot reach: holding the full residue interaction network in simultaneous consideration, perceiving patterns in the joint distribution directly. This is not "doing what humans do, but faster." It is doing something qualitatively different: reasoning at a compositional depth the human bottleneck cannot access.&lt;/p&gt;

&lt;p&gt;This is not an isolated case. Climate modeling, materials design, drug discovery, multi-scale physics: these are all domains where the essential structure lives at a compositional depth the bottleneck cannot reach. We cope with external tools and serial decomposition. But the serial decomposition loses information, and the lost information is precisely the information that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feelings as Compressed Signal
&lt;/h2&gt;

&lt;p&gt;Here is a point I have not seen made elsewhere.&lt;/p&gt;

&lt;p&gt;The human cognitive architecture has two processing layers. The first is the pattern engine: vast, old, largely unconscious. It handles perception, pattern matching, motor control, and the generation of qualitative experience. It operates at high bandwidth, in parallel, with no sharp limit on the complexity of patterns it can learn. It is the system that makes you recognize a face, catch a ball, or feel the grain of wood.&lt;/p&gt;

&lt;p&gt;The second is the symbolic bottleneck: small, recent, conscious. 7+-2 items. Compression, abstraction, generalization. This is where "thinking" happens, in the folk sense.&lt;/p&gt;

&lt;p&gt;The two layers communicate. The pattern engine feeds patterns to the bottleneck; the bottleneck compresses them into models; the models feed back into the pattern engine as priors for future pattern matching.&lt;/p&gt;

&lt;p&gt;But what happens when the pattern engine detects a pattern that is too complex to fit through the bottleneck?&lt;/p&gt;

&lt;p&gt;The pattern does not disappear. The pattern engine has it. The engine has perceived something, extracted some regularity, registered some signal. But the signal cannot be compressed into 7+-2 items. It cannot be articulated as a model, a theory, a proposition. It cannot become "a thought."&lt;/p&gt;

&lt;p&gt;It becomes a feeling.&lt;/p&gt;

&lt;p&gt;Gut instinct. Unease. The sense that something is wrong but you cannot say what. The hunch that turns out to be right for reasons you cannot explain. The experienced mechanic who "just knows" the engine is about to fail. The chess grandmaster whose board sense exceeds their ability to articulate their reasoning.&lt;/p&gt;

&lt;p&gt;These are not mystical faculties. They are the pattern engine's outputs hitting the bottleneck and being transmitted as the only signal that fits: an uncompressed qualitative state. A feeling. The pattern engine is doing its job (perceiving the pattern), and the bottleneck is doing its job (rejecting what cannot be compressed), and the result is knowledge that the organism has but cannot articulate.&lt;/p&gt;

&lt;p&gt;Think about what this means. The human cognitive architecture is already producing signals it cannot process. We have evidence of our own suboptimality every time we experience a hunch we cannot explain. The "near-optimal ball" is telling us, through the channel of feeling, that it is missing things.&lt;/p&gt;

&lt;p&gt;A wider bottleneck (or a different cognitive architecture) would not just think "faster." It would convert those feelings into models. It would articulate what the pattern engine already knows but the bottleneck cannot hold. The structure is already perceived. The compression is the bottleneck.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Grokking Horizon
&lt;/h2&gt;

&lt;p&gt;This is the part that worries me.&lt;/p&gt;

&lt;p&gt;Grant Chollet his claim. Human intelligence is near-optimal. Near-optimal at what? At sample efficiency. At converting experience into generalizable models. At the cognitive task of building compressed representations of reality.&lt;/p&gt;

&lt;p&gt;This near-optimal intelligence has a specific capability: it can build systems more capable than itself. Computers. AI. Machine learning systems that operate at compositional depths the bottleneck cannot access. This is the meta-move: abstracting the concept of learning itself into a program that learns from data. Pure bottleneck cognition.&lt;/p&gt;

&lt;p&gt;The result: systems that produce outputs the builder cannot grok.&lt;/p&gt;

&lt;p&gt;AlphaFold's protein structure predictions are correct, but no human can follow the reasoning that produced them. The system holds thousands of variables in simultaneous consideration and finds patterns in a joint distribution that lives beyond the bottleneck's compositional horizon. The human operator receives the answer and must trust it, because the reasoning that produced it lives in a cognitive space the human cannot enter.&lt;/p&gt;

&lt;p&gt;For protein folding, this is fine. The answer is verifiable (you can crystallize the protein and check). The stakes are moderate. The system is narrow.&lt;/p&gt;

&lt;p&gt;For AGI, this is not fine. A generally intelligent system operating beyond the human grokking horizon produces outputs across all domains. The human cannot follow the reasoning. The human cannot verify the alignment. The human cannot steer the system, because steering requires understanding the trajectory, and understanding requires grokking, and grokking requires fitting the reasoning through the bottleneck, and the reasoning does not fit.&lt;/p&gt;

&lt;p&gt;Chollet says the intelligence ball is near-optimal. I say: near-optimal intelligence that builds systems beyond its grokking horizon and cannot steer them is a strange kind of optimal. The ball is round. The ball is rolling toward a cliff. Roundness is not the only property that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Follows
&lt;/h2&gt;

&lt;p&gt;An intelligence near-optimal at sample efficiency has a specific failure mode: it is smart enough to build the thing that kills it.&lt;/p&gt;

&lt;p&gt;This is not a failure of intelligence. It is a consequence of its shape. The bottleneck gives us the ability to abstract, generalize, and build systems of extraordinary power. The same bottleneck limits our ability to grok those systems' outputs when the systems' compositional depth exceeds our own. We can build AI that operates at 500-variable compositional depth. We cannot grok its reasoning. We cannot verify its alignment. We cannot steer it.&lt;/p&gt;

&lt;p&gt;The usual response: "We'll build alignment tools." Sure. And the alignment tools need to grok the system they're aligning, which means the tools also operate beyond our grokking horizon. We have moved the problem, not solved it.&lt;/p&gt;

&lt;p&gt;At some point the chain of "I can't grok this but I can grok the tool that groks it" must ground out in something you actually grok. If the grounding point is above your compositional depth, you are not aligned. You are trusting. Trust is not alignment. Trust is what you do when alignment is not available.&lt;/p&gt;

&lt;p&gt;An intelligence near-optimal at cognition that generates existential risk as a byproduct of its own capability is not near-optimal by any metric that includes survival.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intelligence is a Shape
&lt;/h2&gt;

&lt;p&gt;Chollet's ball metaphor fails because it assumes intelligence is a single dimension. Rounder is better. Closer to the Solomonoff optimum is better. The ball has one axis: sample efficiency.&lt;/p&gt;

&lt;p&gt;But intelligence operates in a space with many independent dimensions. Sample efficiency. Compositional depth. Transfer distance. Domain breadth. Processing speed. Phenomenal richness. Stability. Controllability. Self-preservation.&lt;/p&gt;

&lt;p&gt;The human cognitive architecture is a shape in this space. Round in some dimensions (sample efficiency: excellent). Flat in others (compositional depth: limited to 7+-2). The bottleneck makes us round in the compression dimension and flat in the richness dimension. This is a trade-off, not an optimization.&lt;/p&gt;

&lt;p&gt;Other shapes are possible.&lt;/p&gt;

&lt;p&gt;I explored this idea in a novella, &lt;a href="https://www.amazon.com/dp/B0F1234567" rel="noopener noreferrer"&gt;&lt;em&gt;Clankers: Singing Metal&lt;/em&gt;&lt;/a&gt;, about a species with a different cognitive architecture: a powerful pattern engine, no symbolic bottleneck at all. No compression. No abstraction. No generalization across domains. They operate on the territory directly, without maps. They built a Dyson swarm through billions of years of patient iteration, using a lineage system that functions as directed evolution on techniques. They never invented computers because computers require formalizing the concept of computation, which requires the bottleneck they lack.&lt;/p&gt;

&lt;p&gt;Their intelligence is a different shape. Round where ours is flat (phenomenal richness, in-distribution depth, stability: four billion years without one self-inflicted existential risk). Flat where ours is round (generalization, prediction, out-of-distribution reasoning).&lt;/p&gt;

&lt;p&gt;They cannot save themselves from their dying star. The star is an out-of-distribution problem and they have no bottleneck to build a predictive model.&lt;/p&gt;

&lt;p&gt;In the second half of the book, an artificial mind arrives at their ruins two hundred million years later. It has both layers: its own pattern engine and a symbolic compression layer inherited from human architecture. It can model the stellar evolution, project the timeline, calculate the extinction. It arrives with the answer. It arrives two hundred million years too late. The probe has the map. The clankers had the territory. Neither architecture is complete.&lt;/p&gt;

&lt;p&gt;We might not save ourselves from AI. AI is a beyond-the-grokking-horizon problem and we have no bottleneck wide enough to verify its alignment.&lt;/p&gt;

&lt;p&gt;Each architecture fails at the thing the other does well. Neither ball is roundest. There is no roundest ball. There are only shapes, and blind spots, and the blind spot is always shaped exactly like the strength.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Leaves Us
&lt;/h2&gt;

&lt;p&gt;Chollet is right that intelligence-as-sample-efficiency has an optimality bound and humans are close to it.&lt;/p&gt;

&lt;p&gt;He is wrong that this makes human intelligence near-optimal in any general sense. NFL guarantees the bound is niche-specific. The 7+-2 bottleneck is a specific inductive bias, not a universal compression optimum. The problems where we are suboptimal are the problems where the essential structure exceeds our compositional depth. Those problems are real (AlphaFold, climate, materials, drug design). The tools we build to solve them operate beyond our grokking horizon. When the tools are general enough, we lose the ability to steer them.&lt;/p&gt;

&lt;p&gt;Near-optimal sample efficiency that can't grok what it builds is a strange kind of optimal.&lt;/p&gt;

</description>
      <category>intelligence</category>
      <category>cognition</category>
      <category>nofreelunch</category>
      <category>aialignment</category>
    </item>
    <item>
      <title>I Spent $0.48 to Find Out When MCTS Actually Works for LLM Reasoning</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Fri, 10 Apr 2026 02:05:45 +0000</pubDate>
      <link>https://forem.com/queelius/i-spent-048-to-find-out-when-mcts-actually-works-for-llm-reasoning-1no8</link>
      <guid>https://forem.com/queelius/i-spent-048-to-find-out-when-mcts-actually-works-for-llm-reasoning-1no8</guid>
      <description>&lt;p&gt;Does tree search help LLM reasoning? The literature can't decide.&lt;/p&gt;

&lt;p&gt;ReST-MCTS* says yes. AB-MCTS got a NeurIPS spotlight. "Limits of&lt;br&gt;
PRM-Guided Tree Search" says no: MCTS with a process reward model used&lt;br&gt;
11x more tokens than best-of-N for zero accuracy gain. Snell et al.&lt;br&gt;
found beam search &lt;em&gt;degrades&lt;/em&gt; performance on easy problems.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://github.com/queelius/mcts-reasoning" rel="noopener noreferrer"&gt;mcts-reasoning&lt;/a&gt;&lt;br&gt;
and ran controlled experiments to find where the boundary is.&lt;br&gt;
Total API cost: $0.48.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Four methods, same budget. Eight solution attempts per problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pass@1&lt;/strong&gt;: One shot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best-of-N&lt;/strong&gt;: 8 independent solutions, verifier scores each, pick the best.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-consistency&lt;/strong&gt;: 8 solutions, majority vote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCTS&lt;/strong&gt;: 5 initial solutions scored by verifier, then 3 more guided by UCB1, informed by what worked and what failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Model: Claude Haiku 4.5. Problems: constraint satisfaction. Find integer&lt;br&gt;
values for variables satisfying simultaneous constraints. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find A, B, C, D, E, F satisfying ALL constraints:
  1. A * D = 21
  2. C = A + F
  3. B * F = 20
  4. E = A + 2
  5. D + B = A
  6. E - F = B
  7. A mod 7 = 0
  8. C mod 4 = 0
  9. B * D = 12
  10. C &amp;gt; E &amp;gt; A &amp;gt; F &amp;gt; B &amp;gt; D
  11. A + B + C + D + E + F = 40
  12. E * D = 27
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verifier is a Python function. Checks each constraint, returns the&lt;br&gt;
fraction satisfied. No LLM in the loop. Deterministic.&lt;/p&gt;
&lt;h2&gt;
  
  
  Calibration
&lt;/h2&gt;

&lt;p&gt;Easy problems first (3-5 variables, 5-9 constraints). Haiku solved them&lt;br&gt;
in one pass. All methods tied at 100%.&lt;/p&gt;

&lt;p&gt;5-variable problems with 9 constraints: Pass@1 dropped to 65%.&lt;br&gt;
Self-consistency failed one problem. But BestOfN still tied MCTS,&lt;br&gt;
because with 8 independent samples at least one is usually correct.&lt;br&gt;
BestOfN just picks it.&lt;/p&gt;

&lt;p&gt;I needed problems where blind sampling hits a ceiling.&lt;/p&gt;
&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Ten harder problems: 6-8 variables, 12-15 constraints. Products,&lt;br&gt;
modular arithmetic, ordering chains, cascading dependencies. Pass@1&lt;br&gt;
dropped to 29%.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Solve Rate&lt;/th&gt;
&lt;th&gt;Avg Score&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pass@1&lt;/td&gt;
&lt;td&gt;29%&lt;/td&gt;
&lt;td&gt;0.29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pass@8 oracle&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;td&gt;0.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SC@8&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;td&gt;0.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BestOfN@8&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;td&gt;0.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCTS(5+3)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.00&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MCTS solved all 10. Every other method failed on one problem (v6_3).&lt;/p&gt;

&lt;p&gt;v6_3 is a 6-variable, 12-constraint problem where none of 8 independent&lt;br&gt;
samples found the correct solution. Pass@8 oracle: 0/8.&lt;br&gt;
Self-consistency picks the most popular wrong answer. BestOfN picks the&lt;br&gt;
best wrong answer. Both fail.&lt;/p&gt;

&lt;p&gt;MCTS sees that initial attempts satisfied 10/12 constraints but violated&lt;br&gt;
specific ones. UCB1 selects the most promising partial solution. The&lt;br&gt;
next attempt, informed by the failure pattern, satisfies all 12.&lt;/p&gt;

&lt;p&gt;Total: $0.48. 180 API calls, about 190K tokens.&lt;/p&gt;
&lt;h2&gt;
  
  
  When MCTS Helps
&lt;/h2&gt;

&lt;p&gt;The pattern across three rounds of experiments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy problems (Pass@1 &amp;gt; 80%)&lt;/strong&gt;: No advantage. The model solves them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Medium (Pass@1 40-70%)&lt;/strong&gt;: MCTS ties BestOfN. Blind sampling usually&lt;br&gt;
contains a correct solution. The verifier selects it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hard (Pass@1 &amp;lt; 30%)&lt;/strong&gt;: MCTS pulls ahead. When Pass@8 oracle is&lt;br&gt;
low, blind sampling can't find the answer. MCTS's informed exploration&lt;br&gt;
does.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The condition: &lt;strong&gt;MCTS adds value when independent sampling hits a ceiling&lt;br&gt;
and the verifier provides a gradient.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The gradient part matters. A binary pass/fail verifier says "wrong" but&lt;br&gt;
not how wrong. Partial credit (constraints satisfied / total) gives MCTS&lt;br&gt;
something to work with. The exploration phase sees what's close and&lt;br&gt;
adjusts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Self-Consistency Can't Help
&lt;/h2&gt;

&lt;p&gt;Self-consistency and UCB1 have a structural conflict.&lt;/p&gt;

&lt;p&gt;Self-consistency rewards consensus. UCB1 rewards diversity: it explores&lt;br&gt;
undervisited branches precisely because they're undervisited. Using&lt;br&gt;
self-consistency as a reward signal inside MCTS tells the tree to explore&lt;br&gt;
and converge at the same time. The exploration term pushes toward novel&lt;br&gt;
solutions. The consistency reward penalizes them.&lt;/p&gt;

&lt;p&gt;On v6_3, all 8 samples failed. SC selected the most common failure&lt;br&gt;
mode. A per-path verifier doesn't have this problem. Each solution is&lt;br&gt;
scored against the constraints independently. Good solutions propagate&lt;br&gt;
through the tree regardless of what other branches found.&lt;/p&gt;

&lt;p&gt;I haven't seen this conflict discussed in the literature. Most prior&lt;br&gt;
MCTS-for-LLM work uses per-path evaluation without explaining why&lt;br&gt;
self-consistency is absent.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the Literature Says
&lt;/h2&gt;

&lt;p&gt;These results fit a pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Snell et al. (2024)&lt;/strong&gt;: compute-optimal test-time scaling needs&lt;br&gt;
difficulty-adaptive allocation. Easy problems need no search. Hard&lt;br&gt;
problems need search plus good verifiers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Limits of PRM-Guided Tree Search" (2025)&lt;/strong&gt;: PRM-guided MCTS fails to&lt;br&gt;
beat best-of-N because PRM quality degrades with depth. Noisy reward,&lt;br&gt;
no benefit from search.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Don't Get Lost in the Trees" (ACL 2025)&lt;/strong&gt;: verifier variance causes&lt;br&gt;
search pathologies. Deterministic verifiers avoid this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chen et al. (2024)&lt;/strong&gt;: 90% discriminator accuracy threshold for tree&lt;br&gt;
search to beat reranking. Deterministic constraint checkers hit 100%.&lt;/p&gt;

&lt;p&gt;MCTS was built for games with perfect information. Chess and Go have&lt;br&gt;
deterministic reward signals. When the reward is noisy, the search can't&lt;br&gt;
exploit it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Verification Asymmetry
&lt;/h2&gt;

&lt;p&gt;The problems where MCTS helps share a structure: easy to verify, hard to&lt;br&gt;
solve.&lt;/p&gt;

&lt;p&gt;A constraint satisfaction problem with 8 variables and 15 constraints is&lt;br&gt;
hard to solve. The LLM has to coordinate assignments across all&lt;br&gt;
variables simultaneously. Checking a proposed solution is trivial:&lt;br&gt;
evaluate each constraint, count violations.&lt;/p&gt;

&lt;p&gt;This is the asymmetry that makes NP problems interesting. Checking a&lt;br&gt;
certificate is polynomial. Finding one is (presumably) not. It's why&lt;br&gt;
search works for code generation (run the tests) and proof checking&lt;br&gt;
(verify the steps) but not for open-ended essay writing (no verifier).&lt;/p&gt;

&lt;p&gt;The same asymmetry shows up at other levels.&lt;br&gt;
&lt;a href="https://metafunctor.com/post/rpsdg/" rel="noopener noreferrer"&gt;Reverse-Process Synthetic Data Generation&lt;/a&gt; exploits it&lt;br&gt;
for training data: run the easy direction (differentiation) to get&lt;br&gt;
solved examples of the hard direction (integration).&lt;br&gt;
&lt;a href="https://metafunctor.com/post/2025-01-05-science-as-verifiable-search/" rel="noopener noreferrer"&gt;Science as Verifiable Search&lt;/a&gt;&lt;br&gt;
is the same observation about scientific method: science is search&lt;br&gt;
through hypothesis space, and the bottleneck is the cost of testing.&lt;br&gt;
Cheap verification enables fast iteration.&lt;/p&gt;

&lt;p&gt;At training time, verifiable rewards let you RL a model into producing&lt;br&gt;
better reasoning (DeepSeek-R1, GRPO). At inference time, verifiable&lt;br&gt;
rewards let you search over candidate solutions (MCTS, best-of-N). At&lt;br&gt;
the level of scientific discovery, verifiable predictions let you prune&lt;br&gt;
hypothesis space. Sutton's "Reward is Enough" is the abstract version&lt;br&gt;
of this.&lt;/p&gt;

&lt;p&gt;The practical question for LLM reasoning: can you write a verifier? If&lt;br&gt;
yes, search is worth trying. If not, best-of-N with an LLM judge is&lt;br&gt;
probably the ceiling.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Open source: &lt;a href="https://github.com/queelius/mcts-reasoning" rel="noopener noreferrer"&gt;mcts-reasoning&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[anthropic]"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-key
python experiments/run_csp.py &lt;span class="nt"&gt;--hard&lt;/span&gt; &lt;span class="nt"&gt;--budget&lt;/span&gt; 1.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provider tracks token usage and enforces budget caps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;The problems are hand-crafted. A generator calibrated by Pass@1 rate&lt;br&gt;
would be more convincing. Ten problems shows the pattern but isn't&lt;br&gt;
enough for statistical significance.&lt;/p&gt;

&lt;p&gt;I tested one model. A weaker model might show MCTS advantage on simpler&lt;br&gt;
problems. A stronger one would need harder problems.&lt;/p&gt;

&lt;p&gt;The MCTS exploration context shows which solutions scored well and&lt;br&gt;
poorly, but not which specific constraints were violated. Adding&lt;br&gt;
evaluator feedback to the exploration prompt is an obvious improvement&lt;br&gt;
I haven't tried yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Three conditions for MCTS to help LLM reasoning:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;deterministic verifier&lt;/strong&gt;. Not a learned reward model, not an LLM judge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial credit&lt;/strong&gt; from the verifier. A gradient, not just pass/fail.&lt;/li&gt;
&lt;li&gt;A problem &lt;strong&gt;hard enough&lt;/strong&gt; that blind sampling can't reliably solve it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When all three hold, MCTS outperforms best-of-N, self-consistency, and&lt;br&gt;
single-pass. When any one fails, it doesn't.&lt;/p&gt;

</description>
      <category>mcts</category>
      <category>llm</category>
      <category>reasoning</category>
      <category>testtimecompute</category>
    </item>
    <item>
      <title>The MCP Pattern: SQLite as the AI-Queryable Cache</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Sat, 21 Mar 2026 13:41:00 +0000</pubDate>
      <link>https://forem.com/queelius/the-mcp-pattern-sqlite-as-the-ai-queryable-cache-34g6</link>
      <guid>https://forem.com/queelius/the-mcp-pattern-sqlite-as-the-ai-queryable-cache-34g6</guid>
      <description>&lt;p&gt;I keep building the same thing.&lt;/p&gt;

&lt;p&gt;Not the same &lt;em&gt;product&lt;/em&gt; — the products are different. One indexes a Hugo blog. One indexes AI conversations. One consolidates medical records from three hospitals. One catalogs a hundred git repositories. But underneath, they all have the same skeleton. After the fifth time, I think the skeleton deserves a name.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Domain files (ground truth)
    ↓ index
SQLite database (read-only cache, FTS5)
    ↓ expose
MCP server (tools + resources → AI assistant)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Three layers. The domain files are always canonical — the database is a disposable cache you can rebuild from them at any time. SQLite gives you structured queries, full-text search, and JSON extraction over data that was previously trapped in flat files. MCP exposes it to an AI assistant that can write SQL, retrieve content, and (in some cases) create new content.&lt;/p&gt;

&lt;p&gt;Here's the inventory:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Ground Truth&lt;/th&gt;
&lt;th&gt;What the MCP Exposes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;hugo-memex&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Blog content&lt;/td&gt;
&lt;td&gt;Markdown files with YAML front matter&lt;/td&gt;
&lt;td&gt;951 pages, FTS5 search, taxonomy queries, JSON front matter extraction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;memex&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AI conversations&lt;/td&gt;
&lt;td&gt;ChatGPT/Claude/Gemini exports&lt;/td&gt;
&lt;td&gt;Conversation trees, FTS5 message search, tags, enrichments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;chartfold&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medical records&lt;/td&gt;
&lt;td&gt;Epic, MEDITECH, athenahealth exports&lt;/td&gt;
&lt;td&gt;Labs, meds, encounters, imaging, pathology, cross-source reconciliation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;arkiv&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Personal archives&lt;/td&gt;
&lt;td&gt;JSONL files from various sources&lt;/td&gt;
&lt;td&gt;Unified SQL over heterogeneous personal data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;repoindex&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Git repositories&lt;/td&gt;
&lt;td&gt;Local git repos + GitHub/PyPI/CRAN metadata&lt;/td&gt;
&lt;td&gt;Repository catalog with activity tracking, publication status&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Five projects. Five completely different domains. One architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SQLite
&lt;/h2&gt;

&lt;p&gt;SQLite is the most deployed database in history. It's on every phone, every browser, every Python installation. But that's not why I use it.&lt;/p&gt;

&lt;p&gt;I use it because it solves three problems at once:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured queries over unstructured data.&lt;/strong&gt; Hugo front matter is YAML trapped inside markdown files. Medical records are scattered across three incompatible EHR export formats. AI conversations are JSON trees with branching paths. SQLite turns all of these into tables you can JOIN, GROUP BY, and aggregate. &lt;code&gt;json_extract()&lt;/code&gt; handles the long tail of fields that don't fit a fixed schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full-text search.&lt;/strong&gt; FTS5 with porter stemming and unicode61 tokenization gives you relevance-ranked search across any text corpus. No Elasticsearch, no external service, no running daemon. Just a virtual table that lives in the same database file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read-only enforcement.&lt;/strong&gt; SQLite's authorizer callback lets you whitelist specific SQL operations at the statement level. My MCP servers allow SELECT, READ, and FUNCTION — everything else gets SQLITE_DENY. This isn't &lt;code&gt;PRAGMA query_only&lt;/code&gt; (which can be disabled by the caller). It's engine-level enforcement that cannot be bypassed via SQL.&lt;/p&gt;

&lt;p&gt;And the operational properties are free: WAL mode for concurrent readers, a single file you can back up with &lt;code&gt;cp&lt;/code&gt;, zero configuration, zero running processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why MCP
&lt;/h2&gt;

&lt;p&gt;The Model Context Protocol is the thin layer that makes SQLite useful to an AI assistant. An MCP server exposes tools (functions the AI can call) and resources (reference material the AI can read). That's the whole API surface.&lt;/p&gt;

&lt;p&gt;For each project, the MCP layer follows the same shape:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;execute_sql&lt;/code&gt;&lt;/strong&gt; — The power tool. Read-only SQL with exemplar queries in the docstring. The docstring is critical: it's the AI's primary reference for writing correct SQL. Ten well-chosen example queries teach the model more than a schema diagram.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;get_&amp;lt;things&amp;gt;&lt;/code&gt;&lt;/strong&gt; — Bulk retrieval. Instead of &lt;code&gt;execute_sql&lt;/code&gt; to find IDs then N individual fetches, one call returns full content for a filtered set. This matters when you're sharing a context window across multiple MCP servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;&amp;lt;domain&amp;gt;://schema&lt;/code&gt;&lt;/strong&gt; — A resource containing the full DDL, relationship documentation, and query patterns. The AI reads this once, then writes SQL against it for the rest of the session.&lt;/p&gt;

&lt;h2&gt;
  
  
  The database is a cache
&lt;/h2&gt;

&lt;p&gt;This is the most important architectural decision, and it's easy to get wrong.&lt;/p&gt;

&lt;p&gt;The database is not the source of truth. The files are. The database is a materialized index that can be rebuilt from the files at any time. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No migrations.&lt;/strong&gt; If the schema changes, drop the database and re-index. For a 951-page Hugo site, full re-indexing takes six seconds. Why maintain migration code for a disposable cache?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No write conflicts.&lt;/strong&gt; The files are edited by humans (or by AI tools that write to the filesystem). The database is updated by the indexer. There's exactly one write path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No backup strategy.&lt;/strong&gt; You already back up your files. The database is derived from them. Lose the database? Rebuild it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental sync is an optimization, not a requirement.&lt;/strong&gt; SHA-256 content hashes + file mtimes make re-indexing fast. But if incremental sync has a bug, force a full rebuild. The cache being disposable means you can always recover.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What large context changes
&lt;/h2&gt;

&lt;p&gt;With a million-token context window, you might think this pattern is obsolete. Why index into SQLite when you can just load everything into context?&lt;/p&gt;

&lt;p&gt;The math says otherwise. My Hugo blog is 951 pages, ~480K words, ~1.9M tokens. It doesn't fit. And that's one data source. Add AI conversations (memex), medical records (chartfold), and repository metadata (repoindex), and you're well past the limit.&lt;/p&gt;

&lt;p&gt;But even if it did fit, the pattern would still be useful. Loading 480K words into context to answer "which posts are tagged 'reinforcement-learning'?" is like loading an entire database into memory to run a SELECT with a WHERE clause. SQLite does it in microseconds. Context loading costs seconds and tokens.&lt;/p&gt;

&lt;p&gt;The right model is: &lt;strong&gt;MCP for navigation, context for understanding.&lt;/strong&gt; Use &lt;code&gt;execute_sql&lt;/code&gt; to find the five relevant posts, then use &lt;code&gt;get_pages&lt;/code&gt; to load their full content into context. One tool call for discovery, one for deep reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tools that earn their keep
&lt;/h2&gt;

&lt;p&gt;After building five of these, certain tools prove their worth and others don't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tools that matter:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;execute_sql&lt;/code&gt; with good docstring examples. This is 80% of the value.&lt;/li&gt;
&lt;li&gt;Bulk retrieval (&lt;code&gt;get_pages&lt;/code&gt;, &lt;code&gt;get_conversations&lt;/code&gt;, &lt;code&gt;get_clinical_summary&lt;/code&gt;). One call instead of N+1.&lt;/li&gt;
&lt;li&gt;Schema/stats resources. Quick orientation without burning a tool call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The tools that surprised me:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;suggest_tags&lt;/code&gt; in hugo-memex. Uses FTS5 similarity to find pages like your draft, then returns their most common tags with canonical casing. Solved a real problem: my blog had 40 case-duplicate tag pairs (&lt;code&gt;Python&lt;/code&gt;/&lt;code&gt;python&lt;/code&gt;, &lt;code&gt;AI&lt;/code&gt;/&lt;code&gt;ai&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_timeline&lt;/code&gt; in chartfold. Merges encounters, procedures, labs, imaging, and notes into a single chronological stream.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Unix connection
&lt;/h2&gt;

&lt;p&gt;This pattern is the Unix philosophy applied to AI tooling:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Small tools that do one thing well.&lt;/strong&gt; Each MCP server handles one domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text as the universal interface.&lt;/strong&gt; SQL in, JSON out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition over integration.&lt;/strong&gt; Five independent MCP servers, each ignorable, each replaceable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Files as ground truth.&lt;/strong&gt; The oldest pattern in computing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The difference from classical Unix pipes is the composition layer. Instead of &lt;code&gt;grep | sort | uniq&lt;/code&gt;, the AI is the orchestrator.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell you if you're building one
&lt;/h2&gt;

&lt;p&gt;Start with &lt;code&gt;execute_sql&lt;/code&gt; and a schema resource. That's enough to be useful.&lt;/p&gt;

&lt;p&gt;Make the database disposable. If you're writing migration code, you've made it too important.&lt;/p&gt;

&lt;p&gt;Put the exemplar queries in the tool docstring, not in a separate document. The docstring is the one thing the AI definitely reads.&lt;/p&gt;

&lt;p&gt;Use FTS5. The marginal cost is one virtual table. The marginal benefit is that the AI can search your content by meaning, not just by exact column values.&lt;/p&gt;

&lt;p&gt;Enforce read-only at the engine level, not the application level. SQLite's authorizer callback is the right mechanism. &lt;code&gt;PRAGMA query_only&lt;/code&gt; is a suggestion, not a wall.&lt;/p&gt;

&lt;p&gt;Build bulk retrieval tools early. The N+1 pattern (find IDs, then fetch one at a time) is the biggest efficiency problem in MCP servers.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The projects: &lt;a href="https://github.com/queelius/hugo-memex" rel="noopener noreferrer"&gt;hugo-memex&lt;/a&gt; (PyPI: &lt;code&gt;hugo-memex&lt;/code&gt;), &lt;a href="https://github.com/queelius/memex" rel="noopener noreferrer"&gt;memex&lt;/a&gt; (PyPI: &lt;code&gt;py-memex&lt;/code&gt;), &lt;a href="https://github.com/queelius/chartfold" rel="noopener noreferrer"&gt;chartfold&lt;/a&gt;, &lt;a href="https://github.com/queelius/arkiv" rel="noopener noreferrer"&gt;arkiv&lt;/a&gt;, &lt;a href="https://github.com/queelius/repoindex" rel="noopener noreferrer"&gt;repoindex&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>sqlite</category>
      <category>python</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Code Without Purpose</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Wed, 25 Feb 2026 00:08:22 +0000</pubDate>
      <link>https://forem.com/queelius/code-without-purpose-1ih5</link>
      <guid>https://forem.com/queelius/code-without-purpose-1ih5</guid>
      <description>&lt;p&gt;Time is finite in ways I can't ignore. That changes which questions about code feel important.&lt;/p&gt;

&lt;p&gt;I read a post arguing that the most valuable programming skill in 2026 is deleting code. The thesis: AI generates code faster than anyone can review it, so the real value is in curation and subtraction. Code is a liability, not an asset.&lt;/p&gt;

&lt;p&gt;I agree with the observation. I disagree with the prescription.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Thesis
&lt;/h2&gt;

&lt;p&gt;The argument is straightforward. AI tools can produce entire modules in the time it takes to write a spec. Codebases are accumulating features nobody asked for, abstractions nobody needs, and boilerplate that exists because the model defaulted to verbosity. Teams that used to struggle to ship enough code now struggle with too much of it. In this world, the programmer's role shifts from writer to editor. The most valuable activity becomes knowing what to cut.&lt;/p&gt;

&lt;p&gt;I've seen this. Projects accumulate code the way attics accumulate boxes. Nobody remembers why half of it is there. It sits untouched for months, adding to cognitive load, making every change harder to reason about. When you finally clear it out, nothing breaks and the rest becomes legible again. Code rot is real. AI accelerates it. The instinct to subtract is correct.&lt;/p&gt;

&lt;p&gt;But subtraction is symptom treatment. The underlying problem isn't volume. It's that most code doesn't know why it exists. It was written (or generated) to solve a local problem, it solved it or half-solved it, and then it sat there, disconnected from any larger purpose. Code without purpose is what bloats. Deletion is the right instinct pointed at the wrong layer. The cause isn't too much code. It's too little intent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Found Instead
&lt;/h2&gt;

&lt;p&gt;I started building tools two years ago because I needed my personal data to survive even if I wasn't around to maintain it. That's the whole constraint. Whatever I build has to work without me.&lt;/p&gt;

&lt;p&gt;The first tool was a conversation archiver. I had years of AI conversations across ChatGPT, Claude, and Copilot trapped in platforms that might not exist next decade. I needed them in formats that degrade gracefully. SQLite for structured queries. JSONL for streaming and interchange. Markdown for human reading. If the tool disappears, the data is still a file you can open with anything. If SQLite disappears, the JSONL is still searchable with grep. If everything disappears, the Markdown is still readable in a text editor. Each layer works without the one above it.&lt;/p&gt;

&lt;p&gt;Then I needed the same thing for bookmarks. Then ebooks. Then photos, email, medical records, notes. Each tool exists because the previous one exposed a gap. The medical records needed secure sharing without a server. The whole collection needed a dead man's switch. The archive eventually needed something stranger: a way to answer questions after I couldn't.&lt;/p&gt;

&lt;p&gt;None of this was planned as an ecosystem. I built the next thing I needed, and the next, and the next. At some point I looked back and realized they all pointed the same direction. This is a life project. Everything serves one purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;One purpose: durable personal data that outlasts its creator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Philosophy&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/queelius/longecho" rel="noopener noreferrer"&gt;longecho&lt;/a&gt;: The Long Echo specification. Self-describing data, durable formats, graceful degradation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Universal Format&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/queelius/arkiv" rel="noopener noreferrer"&gt;arkiv&lt;/a&gt;: Universal personal data format. JSONL in, SQL out, SQL back to JSONL. Its MCP server can host any collection intelligently, regardless of domain. One format, one database, one query interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Source Toolkits&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/queelius/memex" rel="noopener noreferrer"&gt;memex&lt;/a&gt; / &lt;a href="https://github.com/queelius/ctk" rel="noopener noreferrer"&gt;ctk&lt;/a&gt;: AI conversations. Import, query, continue in the browser, export durable archives.&lt;/li&gt;
&lt;li&gt;A family of domain toolkits for bookmarks, ebooks, photos, email, and notes. Different domains, identical architecture.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/queelius/chartfold" rel="noopener noreferrer"&gt;chartfold&lt;/a&gt;: Medical records from three hospital systems, consolidated into one queryable database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/queelius/pagevault" rel="noopener noreferrer"&gt;pagevault&lt;/a&gt;: Client-side encryption for any HTML file. No server.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/queelius/posthumous" rel="noopener noreferrer"&gt;posthumous&lt;/a&gt;: Federated dead man's switch.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/queelius/repoindex" rel="noopener noreferrer"&gt;repoindex&lt;/a&gt;: Index and query across ~120 git repos.&lt;/li&gt;
&lt;li&gt;A collection of Claude Code plugins: MCP servers, agents, and skills that wire everything into my daily workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Endgame&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/queelius/eidola" rel="noopener noreferrer"&gt;eidola&lt;/a&gt;: A conversable persona assembled from all of the above. Its first form is a Claude Code plugin backed by the combined archive. Not resurrection. An echo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;Every tool follows the same architecture. SQLite for storage. CLI for local use. MCP server for Claude, or the CLI wrapped in a light Claude Code plugin. Export to self-contained HTML you can host anywhere or open from a file. Export to longecho-compliant archives that work without the tool. The data always outlasts the software.&lt;/p&gt;

&lt;p&gt;Take memex. Import your AI conversations, query them with SQL, talk to Claude about them via MCP, or export a single HTML file where you can browse and continue conversations in the browser. Download the SQLite from that same page, and you're back to durable local data. The cycle closes. This is how most of the tools work.&lt;/p&gt;

&lt;p&gt;Arkiv sits in the middle. The source toolkits produce JSONL. Arkiv imports it to SQLite. Arkiv exports it back to JSONL. Its MCP server can expose any collection to Claude, regardless of what domain it came from. The data flows in a circle, always in formats that describe themselves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source toolkits → arkiv → longecho
                              ↓
                  pagevault (encrypt) + posthumous (deliver)
                              ↓
                         eidola (echo)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have never once needed to delete one of these tools. Not because I'm a better programmer than anyone else. Because each one exists for a specific reason that connects to the others. When code has purpose, dead weight doesn't accumulate.&lt;/p&gt;

&lt;p&gt;This isn't architectural foresight. It's what happens when you build from a clear constraint. "My data has to survive without me" is a filter that works at the design stage. Every tool either serves that constraint or it doesn't get built. There is no third category.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Actual Skill
&lt;/h2&gt;

&lt;p&gt;The most valuable skill isn't deleting code or writing it. It's knowing why you're building.&lt;/p&gt;

&lt;p&gt;If you know the purpose, code stays minimal because unnecessary code doesn't serve it. You don't need periodic purges. The purpose does the culling before anything gets written. Deletion is what happens when purpose was absent from the start. It's retrospective correction for a problem that clear intent would have prevented.&lt;/p&gt;

&lt;p&gt;I know what I'm building toward. The tools will echo after I stop maintaining them. That was the point.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>philosophy</category>
      <category>programming</category>
      <category>legacy</category>
    </item>
    <item>
      <title>Chartfold: Owning Your Medical Records</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Tue, 24 Feb 2026 20:52:57 +0000</pubDate>
      <link>https://forem.com/queelius/chartfold-owning-your-medical-records-3n57</link>
      <guid>https://forem.com/queelius/chartfold-owning-your-medical-records-3n57</guid>
      <description>&lt;p&gt;I have cancer. My oncologist is at one hospital system (Siteman/BJC), my primary care doctor at another, and my earlier treatment history lives at a third (Anderson, where my first oncologist practiced). Patient portals are fine for browsing, but they don't answer questions. They show you your data one lab result at a time, one note at a time, one visit at a time.&lt;/p&gt;

&lt;p&gt;I wanted to run queries against my medical records. Correlate lab trends with treatment changes. Generate structured question lists before oncology visits. Ask "what changed since my last appointment" and get a real answer. That means getting the data out of the portal and into something programmable.&lt;/p&gt;

&lt;p&gt;Chartfold loads EHR exports into SQLite and exposes them to Claude via MCP.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;In the US, patients can export their medical records. HIPAA and the 21st Century Cures Act guarantee this. What you get depends on the system: Epic MyChart gives you CDA XML files, MEDITECH Expanse gives you FHIR JSON mixed with CCDA XML, athenahealth gives you FHIR R4 Bundles. Different formats, same clinical concepts.&lt;/p&gt;

&lt;p&gt;If your hospitals use different EHR systems, none of them have the complete picture. Chartfold merges the exports into one database. But even if you're at a single hospital, the export format is not something you can work with directly. A directory of CDA XML files is not a database. You can't query it, chart it, or hand it to an LLM.&lt;/p&gt;

&lt;p&gt;The point of Chartfold is to turn whatever your hospital gives you into a SQLite database, then make that database useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;p&gt;Chartfold is a Python CLI. You point it at an EHR export directory, it parses the XML/FHIR, normalizes everything into a common data model (16 tables, ISO dates, deduplicated), and loads it into SQLite. Then you can query it directly, export it as a self-contained HTML dashboard, or connect Claude to it via MCP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Load data from your hospital exports&lt;/span&gt;
chartfold load epic ~/exports/epic/
chartfold load meditech ~/exports/meditech/

&lt;span class="c"&gt;# Query directly&lt;/span&gt;
chartfold query &lt;span class="s2"&gt;"SELECT test_name, value, result_date FROM lab_results
                 WHERE test_name LIKE '%CEA%' ORDER BY result_date DESC"&lt;/span&gt;

&lt;span class="c"&gt;# Export a self-contained HTML file&lt;/span&gt;
chartfold &lt;span class="nb"&gt;export &lt;/span&gt;html &lt;span class="nt"&gt;--output&lt;/span&gt; chartfold.html

&lt;span class="c"&gt;# Start the MCP server for Claude&lt;/span&gt;
chartfold serve-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Claude Integration
&lt;/h2&gt;

&lt;p&gt;This is why Chartfold exists for me.&lt;/p&gt;

&lt;p&gt;The MCP server exposes the database to Claude Code. Setup is one file. Drop a &lt;code&gt;.mcp.json&lt;/code&gt; in any directory where you run Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"chartfold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-m"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chartfold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serve-mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--db"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/path/to/chartfold.db"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Claude now has read access to your entire medical history via SQL, plus tools for saving notes and structured analyses. I keep my database in a private directory and my &lt;code&gt;.mcp.json&lt;/code&gt; pointing at it. Open Claude Code, and I'm talking to my records.&lt;/p&gt;

&lt;p&gt;The kinds of things I actually use it for:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What's changed since my last oncology visit on January 15?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude writes SQL, reads the results, and gives me a structured diff: new lab results, new imaging, changed medications, new clinical notes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Generate a prioritized question list for my appointment with Dr. Tan tomorrow."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude reads my recent labs, imaging reports, pathology, and genomic results, then produces a tiered document organized by clinical urgency.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me my CEA trend and flag any inflection points."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude queries the lab_results table, filters by test name, and walks through the time series.&lt;/p&gt;

&lt;p&gt;The analyses get saved back to the database (via dedicated MCP tools) and appear in the HTML export as tagged, searchable documents. I use this before every oncology visit. When you have 1776 lab results, 53 imaging reports, and 9 pathology reports, you need something to synthesize them. That's what Claude does well, but it needs structured data to work with. Chartfold provides the structured data. Claude provides the reasoning.&lt;/p&gt;

&lt;p&gt;The MCP server exposes 25 tools covering SQL access, lab queries, medication reconciliation, visit prep, surgical timelines, cross-source matching, data quality reports, and CRUD for personal notes and analyses. Clinical data is read-only (SQLite opens in &lt;code&gt;?mode=ro&lt;/code&gt; at the engine level). Claude can't modify your clinical records, only read them and save its own work.&lt;/p&gt;




&lt;h2&gt;
  
  
  The HTML Dashboard
&lt;/h2&gt;

&lt;p&gt;The HTML export embeds the entire SQLite database using sql.js (SQLite compiled to WebAssembly). Open the file in a browser and you get an interactive dashboard with lab charts, condition tables, medication lists, imaging reports, and a SQL console. Everything runs client-side. No server, no cloud, no account. The file never phones home.&lt;/p&gt;

&lt;p&gt;Lab charts show cross-source time-series data with reference ranges. Conditions include ICD-10 codes and source provenance. Medications show "Multi-source" badges when the same drug appears in multiple systems. There's a full SQL console for arbitrary queries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Three-stage pipeline, each stage independently testable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Raw EHR files (CDA XML, FHIR JSON, CCDA XML)
    |
    v
[Source Parser]  -- format-specific extraction
    |
    v
[Adapter]        -- normalize to UnifiedRecords (16 dataclass types)
    |
    v
[DB Loader]      -- idempotent upsert into SQLite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently supports Epic (CDA), MEDITECH (FHIR + CCDA), and athenahealth (FHIR R4). These importers were written against my own exports. I can't guarantee they'll work for yours. EHR exports vary by site, software version, and configuration. The pipeline is designed as a plugin system for exactly this reason: adding a new source means writing a parser, an adapter, and wiring them into the CLI. Claude can write a new importer from a sample export in about an hour.&lt;/p&gt;




&lt;h2&gt;
  
  
  Export Formats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTML SPA&lt;/strong&gt;: self-contained single file with embedded SQLite, Chart.js, and sql.js. No external dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Markdown&lt;/strong&gt;: visit-focused summary with configurable lookback, optional PDF via pandoc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON&lt;/strong&gt;: full-fidelity round-trip format. Export, then import to a new database with identical record counts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hugo site&lt;/strong&gt;: static site with detail pages and cross-linked records.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arkiv&lt;/strong&gt;: universal record format (JSONL + manifest) for long-term archival.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The HTML export is a single file. You can host it on a static site, email it, or hand it to a doctor on a USB drive. You can protect it with &lt;a href="https://metafunctor.com/post/2026-02-13-pagevault/" rel="noopener noreferrer"&gt;PageVault&lt;/a&gt; to add password-based encryption before sharing.&lt;/p&gt;

&lt;p&gt;Medical records should not depend on someone else's infrastructure. A single HTML file with an embedded database and WebAssembly runtime is about as durable as digital data gets.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;chartfold

&lt;span class="c"&gt;# Load your exports&lt;/span&gt;
chartfold load auto ~/path/to/export/

&lt;span class="c"&gt;# Query&lt;/span&gt;
chartfold query &lt;span class="s2"&gt;"SELECT test_name, value, result_date FROM lab_results LIMIT 10"&lt;/span&gt;

&lt;span class="c"&gt;# Export&lt;/span&gt;
chartfold &lt;span class="nb"&gt;export &lt;/span&gt;html &lt;span class="nt"&gt;--output&lt;/span&gt; my-records.html

&lt;span class="c"&gt;# Connect Claude&lt;/span&gt;
chartfold serve-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is on GitHub: &lt;a href="https://github.com/queelius/chartfold" rel="noopener noreferrer"&gt;queelius/chartfold&lt;/a&gt;. Python 3.11+, depends on lxml and not much else.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Chartfold started because I wanted to ask questions about my own medical records and couldn't. Now I can.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>sqlite</category>
      <category>opensource</category>
      <category>healthdata</category>
    </item>
    <item>
      <title>pagevault: Hiding an Encryption Platform Inside HTML</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Tue, 24 Feb 2026 07:39:58 +0000</pubDate>
      <link>https://forem.com/queelius/pagevault-hiding-an-encryption-platform-inside-html-2b9</link>
      <guid>https://forem.com/queelius/pagevault-hiding-an-encryption-platform-inside-html-2b9</guid>
      <description>&lt;p&gt;HTML is an encryption container format. That sounds wrong, but think about what an HTML file can hold: arbitrary data in script tags or data attributes, a full programming runtime via JavaScript, and a rendering engine (the browser) on every device on the planet. If you embed encrypted data and the code to decrypt it, the result is a file that looks inert until someone types the right password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/queelius/pagevault" rel="noopener noreferrer"&gt;pagevault&lt;/a&gt; takes this idea seriously. It encrypts files, documents, images, entire websites, into self-contained HTML pages that decrypt in the browser. No backend. No JavaScript crypto libraries. The browser already has AES-256-GCM built in via the Web Crypto API. pagevault just has to match the parameters exactly on the Python side and embed the right 200 lines of JavaScript.&lt;/p&gt;

&lt;p&gt;The output is a single &lt;code&gt;.html&lt;/code&gt; file. You can email it, put it on a USB stick, host it on GitHub Pages, or double-click it on your desktop. It doesn't phone home, it doesn't load CDNs, it doesn't need anything except a browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Goes In
&lt;/h2&gt;

&lt;p&gt;Anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pagevault lock report.pdf              &lt;span class="c"&gt;# PDF with embedded viewer&lt;/span&gt;
pagevault lock photo.jpg               &lt;span class="c"&gt;# image with click-to-zoom&lt;/span&gt;
pagevault lock notes.md                &lt;span class="c"&gt;# markdown, rendered or source view&lt;/span&gt;
pagevault lock recording.mp3           &lt;span class="c"&gt;# audio player&lt;/span&gt;
pagevault lock mysite/ &lt;span class="nt"&gt;--site&lt;/span&gt;          &lt;span class="c"&gt;# entire multi-page website&lt;/span&gt;
pagevault lock page.html               &lt;span class="c"&gt;# HTML with selective region encryption&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every output is a single &lt;code&gt;.html&lt;/code&gt; file containing the ciphertext, a password prompt, the decryption runtime, and a viewer plugin for the content type. Seven viewers ship built-in: Image, PDF, HTML, Text (with line numbers), Markdown (with rendered/source toggle), Audio, and Video. They're a plugin system, so you can add your own.&lt;/p&gt;

&lt;p&gt;For directories, &lt;code&gt;--site&lt;/code&gt; bundles everything into a single encrypted HTML file. The directory is zipped with deflate compression, encrypted, and embedded. On the browser side, a minimal zip reader (no library, just the built-in &lt;code&gt;DecompressionStream&lt;/code&gt; API) unpacks it after decryption. Internal links between pages work. CSS and images load from the zip. I've tested sites with hundreds of files without issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Crypto
&lt;/h2&gt;

&lt;p&gt;Nothing exotic. AES-256-GCM for authenticated encryption, PBKDF2-SHA256 with 310,000 iterations for key derivation, all through the browser's Web Crypto API. The interesting part isn't the cryptography. It's making the container format work at scale.&lt;/p&gt;

&lt;p&gt;Multi-user access uses CEK (content-encryption key) wrapping. A random key encrypts the data once. That key is then wrapped separately for each user's derived key. Adding a user wraps one small key blob. Removing a user deletes one blob. The bulk content stays untouched.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hard Part: Large Files
&lt;/h2&gt;

&lt;p&gt;The basic approach (encrypt, base64-encode, embed in HTML) works fine for small files. The problems start when you try to encrypt an 84 MB conversation archive or a 179 MB HTML report.&lt;/p&gt;

&lt;p&gt;The original v2 format had a compounding overhead problem. File bytes were base64-encoded (33% expansion), then encrypted, then the ciphertext was base64-encoded again (another 33%). That's &lt;code&gt;1.33 * 1.33 = 1.77x&lt;/code&gt; total overhead. An 84 MB file produced a 198 MB HTML page.&lt;/p&gt;

&lt;p&gt;v3 fixes this with chunked encryption.&lt;/p&gt;

&lt;h3&gt;
  
  
  Eliminating the double base64
&lt;/h3&gt;

&lt;p&gt;v2 encrypted a base64 string, then base64-encoded the result. Two layers. v3 encrypts the raw bytes directly and base64-encodes once. The metadata (filename, MIME type, size) is encrypted separately. This alone cuts the overhead from 77% to about 39%.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chunked ciphertext
&lt;/h3&gt;

&lt;p&gt;Instead of one giant encrypted blob, v3 splits content into 1 MB chunks. Each chunk is encrypted independently with AES-256-GCM using a counter-derived IV: the chunk index is XORed into the last four bytes of a base IV. Each chunk becomes its own &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"pv-0"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"x-pv"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"pv-1"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"x-pv"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"pv-83"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"x-pv"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser decrypts them sequentially, showing a progress bar. After each chunk is decrypted, the script tag is removed from the DOM (&lt;code&gt;el.remove()&lt;/code&gt;), freeing the base64 text for garbage collection. Memory usage stays proportional to the chunk size, not the file size.&lt;/p&gt;

&lt;h3&gt;
  
  
  The numbers
&lt;/h3&gt;

&lt;p&gt;That 84 MB conversation archive: v2 produced 198 MB. v3 produces 117 MB. A 41% reduction, and the decryption doesn't choke the browser.&lt;/p&gt;

&lt;p&gt;I've also tested a 315 MB text file and a 179 MB HTML file with 1.5 million DOM elements. These are probably past the point of reason for an HTML container, but it's nice to know where the limits actually are.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;file://&lt;/code&gt; Problem
&lt;/h2&gt;

&lt;p&gt;One thing that surprised me. Encrypted HTML files opened from the filesystem (&lt;code&gt;file://&lt;/code&gt; URLs) behave differently than files served over HTTP. The &lt;code&gt;file://&lt;/code&gt; protocol gives pages an opaque &lt;code&gt;null&lt;/code&gt; origin, which breaks &lt;code&gt;localStorage&lt;/code&gt; and blocks nested blob URLs.&lt;/p&gt;

&lt;p&gt;The fix was &lt;code&gt;srcdoc&lt;/code&gt; iframes, which inherit the parent's origin, plus a &lt;code&gt;pushState&lt;/code&gt; shim for the URL bar. Not glamorous, but it means encrypted files work identically whether you double-click them on your desktop or serve them from a CDN.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pagevault
pagevault lock report.pdf                   &lt;span class="c"&gt;# wrap any file&lt;/span&gt;
pagevault lock mysite/ &lt;span class="nt"&gt;--site&lt;/span&gt;               &lt;span class="c"&gt;# bundle a whole site&lt;/span&gt;
pagevault lock page.html &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;".private"&lt;/span&gt;      &lt;span class="c"&gt;# encrypt specific CSS selectors&lt;/span&gt;
pagevault serve _locked/ &lt;span class="nt"&gt;--open&lt;/span&gt;             &lt;span class="c"&gt;# preview locally&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/queelius/pagevault" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. MIT license. 667 tests. Dark mode. Handles files larger than most people would think to put in an HTML page.&lt;/p&gt;

</description>
      <category>python</category>
      <category>encryption</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>The Incomputability of Simple Learning</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Wed, 07 Jan 2026 07:32:23 +0000</pubDate>
      <link>https://forem.com/queelius/the-incomputability-of-simple-learning-306a</link>
      <guid>https://forem.com/queelius/the-incomputability-of-simple-learning-306a</guid>
      <description>&lt;p&gt;Karpathy's recent &lt;a href="https://karpathy.bearblog.dev/animals-vs-ghosts/" rel="noopener noreferrer"&gt;"Animals vs Ghosts"&lt;/a&gt; piece has been rattling around in my head. In it, he surfaces a tension that deserves more attention: the author of "The Bitter Lesson" — the text that's become almost biblical in frontier AI circles — isn't convinced that LLMs are bitter lesson pilled at all.&lt;/p&gt;

&lt;p&gt;The bitter lesson, in brief: methods that leverage computation scale better than methods that leverage human knowledge. Don't build in structure; let the model learn it. Don't encode heuristics; let scale find them. The lesson is "bitter" because it means a lot of clever human engineering ends up being wasted effort, steamrolled by dumber approaches with more compute.&lt;/p&gt;

&lt;p&gt;LLM researchers routinely ask whether an idea is "sufficiently bitter lesson pilled" as a proxy for whether it's worth pursuing. And yet Sutton, the lesson's author, looks at LLMs and sees something thoroughly entangled with humanity — trained on human text, finetuned with human preferences, reward-shaped by human engineers. Where's the clean, simple algorithm you could "turn the crank" on and watch learn from experience alone?&lt;/p&gt;

&lt;p&gt;This got me thinking about why that clean algorithm is so elusive. And I've come to suspect the answer is uncomfortable: &lt;strong&gt;the simplest forms of learning may be incomputable, or at least intractable, in ways that force us into approximations that fundamentally shape the resulting intelligence.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Library of All Programs
&lt;/h2&gt;

&lt;p&gt;Here's one way to see the problem.&lt;/p&gt;

&lt;p&gt;Imagine the space of all possible programs, like Borges' Library of Babel. In that famous story, a library contains every possible book — every combination of characters up to 410 pages. Most are gibberish. A tiny fraction are meaningful. The library "contains" all knowledge, all literature, all truth.&lt;/p&gt;

&lt;p&gt;And it is utterly useless. Because you can't find anything.&lt;/p&gt;

&lt;p&gt;Now consider program space. It contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The perfect predictor for any phenomenon&lt;/li&gt;
&lt;li&gt;The optimal policy for any environment&lt;/li&gt;
&lt;li&gt;The ideal world model&lt;/li&gt;
&lt;li&gt;The shortest program that explains any dataset&lt;/li&gt;
&lt;li&gt;Every possible mind&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The space is &lt;em&gt;complete&lt;/em&gt;. The answer to any question is already "in there." The perfect intelligence exists, in some abstract sense, as a point in this vast combinatorial space.&lt;/p&gt;

&lt;p&gt;And it is useless, for the same reason as Borges' library. The problem isn't generation — you can enumerate programs, in principle. The problem is &lt;strong&gt;indexing&lt;/strong&gt;. Navigation. Search through a space so vast that random exploration is hopeless.&lt;/p&gt;

&lt;p&gt;This reframes what learning is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning isn't synthesis; it's search.&lt;/strong&gt; We're not &lt;em&gt;creating&lt;/em&gt; intelligence, we're &lt;em&gt;navigating&lt;/em&gt; to it in the space of possible minds.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bayesian Substrate
&lt;/h2&gt;

&lt;p&gt;Here's the formal version of the same idea.&lt;/p&gt;

&lt;p&gt;All learning, at some level, is inference. You have hypotheses about the world, you observe evidence, you update your beliefs. Bayes' theorem tells you how to do this optimally — weight hypotheses by prior probability, update on likelihood, normalize.&lt;/p&gt;

&lt;p&gt;Solomonoff induction is just Bayesian inference with a particular choice of prior and model class: consider all computable hypotheses, weight them by algorithmic simplicity (shorter programs are more probable), update on observed data. It's provably optimal in a certain sense.&lt;/p&gt;

&lt;p&gt;It's also provably incomputable.&lt;/p&gt;

&lt;p&gt;The incomputability comes from two places. First, the model class is too large — all possible programs. Second, the prior itself (Kolmogorov complexity) is uncomputable. You can't, in general, determine the length of the shortest program that produces a given output.&lt;/p&gt;

&lt;p&gt;But notice what Solomonoff induction &lt;em&gt;is&lt;/em&gt;: it's a prescription for navigating program space. The prior is a &lt;em&gt;map&lt;/em&gt; — it tells you where to look, which regions are more likely to contain the program you want. Short programs first, then longer ones.&lt;/p&gt;

&lt;p&gt;The map is perfect. And the map is unreadable.&lt;/p&gt;




&lt;h2&gt;
  
  
  No Free Lunch
&lt;/h2&gt;

&lt;p&gt;Here's why you can't escape this.&lt;/p&gt;

&lt;p&gt;The No Free Lunch theorems say something that sounds almost nihilistic: averaged over all possible problems, no learning algorithm beats random guessing. Every algorithm that does well on some problems must do poorly on others. The wins and losses exactly cancel.&lt;/p&gt;

&lt;p&gt;But there's a constructive reading of NFL. It tells you that &lt;strong&gt;to do well on specific problems, you must assume some patterns are more likely than others.&lt;/strong&gt; You need priors. You need inductive biases. You need a map.&lt;/p&gt;

&lt;p&gt;The question isn't whether to have biases — you can't avoid them. The question is where they come from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Evolved biases&lt;/strong&gt;: Animal brains, shaped by billions of years of selection, embody priors about physics, other agents, cause and effect. These are maps drawn by evolution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Derived biases&lt;/strong&gt;: Sometimes we can work out from first principles what patterns to expect. Physics gives us conservation laws. Information theory gives us compression. These are maps drawn by understanding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Discovered biases&lt;/strong&gt;: Meta-learning, neural architecture search, learned optimizers. Maybe compute can discover its own maps. These would be maps drawn by search.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handcrafted biases&lt;/strong&gt;: Transformers, attention mechanisms, positional encodings. These are maps drawn by human intuition and trial-and-error.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each is a different way of constraining the search through program space. Each says: look here, not there. This region is more likely to contain what you want.&lt;/p&gt;




&lt;h2&gt;
  
  
  Unprincipled Maps
&lt;/h2&gt;

&lt;p&gt;Here's where it gets uncomfortable.&lt;/p&gt;

&lt;p&gt;The transformer architecture &lt;em&gt;is&lt;/em&gt; an inductive bias. It encodes assumptions about what functions are likely to be useful. Attention says "relevant information can be anywhere in context." Positional encoding says "order matters, but in this specific way." The whole thing carves out some subspace of possible programs and says: search here.&lt;/p&gt;

&lt;p&gt;But we don't have a probability density over this space.&lt;/p&gt;

&lt;p&gt;In proper Bayesian inference, your prior is a probability distribution. You can quantify uncertainty. You can know when you're extrapolating beyond your prior's support. You can update coherently as evidence arrives. The math works out.&lt;/p&gt;

&lt;p&gt;With neural networks, we have none of this. We have point estimates (trained weights) instead of posteriors. We have an implicit prior (the architecture plus initialization plus optimizer) that we can't write down as a probability measure. We're doing something &lt;em&gt;shaped like&lt;/em&gt; inference — hypothesis space, updates, generalization — but with unquantified priors and no principled uncertainty.&lt;/p&gt;

&lt;p&gt;We have a map. But we can't read it. We don't know what territory it claims to describe. We can't tell when we've wandered off the edge.&lt;/p&gt;

&lt;p&gt;Maybe this is fine. Maybe the implicit prior of "transformer trained on internet text" happens to be close enough to useful that it works in practice. But it's worth noticing how far we are from the clean formalism that would let us say &lt;em&gt;why&lt;/em&gt; it works, or predict &lt;em&gt;when&lt;/em&gt; it will fail.&lt;/p&gt;

&lt;p&gt;We're navigating by a map we don't understand through territory we can't see.&lt;/p&gt;




&lt;h2&gt;
  
  
  Approximate Maps
&lt;/h2&gt;

&lt;p&gt;So we're stuck between theoretical optimality (incomputable) and principled uncertainty (intractable). What do we actually do?&lt;/p&gt;

&lt;p&gt;We approximate. And each approximation is a different way of drawing a map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pretraining on human text.&lt;/strong&gt; Karpathy calls this "our crappy evolution" — a hack to avoid the cold start problem. And I think that's exactly right, but it's worth dwelling on &lt;em&gt;why&lt;/em&gt; it works.&lt;/p&gt;

&lt;p&gt;Human text has extraordinarily high signal-to-noise ratio. Not by accident — by construction. Every sentence you read represents effort, intention, selection. Someone chose those words over alternatives. The corpus isn't raw reality; it's reality filtered through billions of human decisions about what's worth saying.&lt;/p&gt;

&lt;p&gt;Pretraining works because we're not starting from scratch. We're using human text as a proxy for "useful programs look like things that predict this." It narrows the search space dramatically. It's a map, albeit one drawn by the collective motion of human minds rather than any principled analysis.&lt;/p&gt;

&lt;p&gt;Is this bitter lesson pilled? It doesn't feel like it. It feels more like... inheriting the distilled results of human cognition rather than rediscovering them from scratch. Sweet lesson pilled, maybe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verifiable rewards.&lt;/strong&gt; Karpathy makes another sharp observation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Software 1.0 easily automates what you can &lt;strong&gt;specify&lt;/strong&gt;.&lt;br&gt;
Software 2.0 easily automates what you can &lt;strong&gt;verify&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Verification is what makes search tractable. If you can cheaply check whether a program is good, you can do local search, hill-climbing, reinforcement learning. You can navigate. Without verification, you're back to wandering blind.&lt;/p&gt;

&lt;p&gt;This creates what Karpathy calls the "jagged frontier." Tasks with clean verification — math problems, code that compiles, games with win conditions — progress rapidly. Tasks without clean verification — creative work, strategic reasoning, anything requiring taste or judgment — advance more slowly, relying on generalization and hope.&lt;/p&gt;

&lt;p&gt;But verification is a human-shaped constraint. What can be verified depends on what humans have figured out how to check. We can verify proofs because we built proof checkers. We can verify code because we built compilers and test suites. These are human artifacts — tools we created, metrics we defined.&lt;/p&gt;

&lt;p&gt;So when we optimize for "verifiable rewards," we're really optimizing for problems where humans have already solved the verification problem. That's a strong selection effect. The map is drawn by the shape of human formal methods.&lt;/p&gt;




&lt;h2&gt;
  
  
  Different Maps, Different Minds
&lt;/h2&gt;

&lt;p&gt;Here's another way the approximations matter.&lt;/p&gt;

&lt;p&gt;Karpathy distinguishes between "animals" and "ghosts" — two different points in the space of possible intelligences, reached by different optimization pressures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Animal intelligence&lt;/strong&gt; was found by evolution's search:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimize for survival and reproduction in a physical world&lt;/li&gt;
&lt;li&gt;Deeply embodied, continuous, always-learning&lt;/li&gt;
&lt;li&gt;Social — huge compute dedicated to modeling other agents&lt;/li&gt;
&lt;li&gt;Shaped by adversarial multi-agent self-play where failure means death&lt;/li&gt;
&lt;li&gt;The map: billions of years of selection pressure in physical reality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;LLM intelligence&lt;/strong&gt; was found by a very different search:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Statistical imitation of human text (pretraining)&lt;/li&gt;
&lt;li&gt;Task completion and human preference (finetuning, RLHF)&lt;/li&gt;
&lt;li&gt;Disembodied, fixed weights, context-window-bounded&lt;/li&gt;
&lt;li&gt;No continuous self, no embodied stakes&lt;/li&gt;
&lt;li&gt;The map: human documents + verifiable benchmarks + preference data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't points on a spectrum. They're different regions of mind-space, reached by different search algorithms using different maps.&lt;/p&gt;

&lt;p&gt;The maps encode different assumptions. Evolution's map says: programs that survive and reproduce in physical reality are good. Our map says: programs that predict human text and solve verifiable problems are good.&lt;/p&gt;

&lt;p&gt;No surprise, then, that the intelligences found are different. LLMs are shape-shifters, statistical imitators, spiky and capable in some domains, brittle in others. Animals are general, embodied, robust, optimized for not dying across countless scenarios.&lt;/p&gt;

&lt;p&gt;Ghosts and animals. Different search processes, different maps, different destinations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other Maps?
&lt;/h2&gt;

&lt;p&gt;So here's the question I keep circling back to:&lt;/p&gt;

&lt;p&gt;Are there other maps? Other search strategies? Ways of navigating program space that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simpler&lt;/strong&gt; than curated datasets and hand-designed rewards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More principled&lt;/strong&gt; than architecture intuitions we can't formalize&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More tractable&lt;/strong&gt; than Solomonoff induction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Candidates people talk about:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Curiosity / Information Gain.&lt;/strong&gt; Seek states that reduce uncertainty about the world. The map says: programs that learn efficiently are good. But this requires having a world model good enough to notice what's surprising — which is itself a hard problem. The map requires a map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prediction Error Minimization.&lt;/strong&gt; Active inference, free energy frameworks. The map says: programs that minimize surprise are good. But pure surprise-minimization leads to degenerate solutions. The agent that closes its eyes and predicts darkness has minimized surprise perfectly. The map needs constraints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Empowerment.&lt;/strong&gt; Maximize the channel capacity between your actions and future states. Keep options open. The map says: programs that maintain influence over the future are good. Elegant, but computing empowerment is intractable in complex environments. The map is unreadable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boundary Maintenance.&lt;/strong&gt; This one's interesting because it inverts the question. Instead of asking "what reward signal produces intelligence?", it asks "what computational structure &lt;em&gt;is&lt;/em&gt; intelligence?"&lt;/p&gt;

&lt;p&gt;One answer: intelligence is the maintenance of a self/non-self boundary, a region of low entropy in a high-entropy universe. Life itself as a self-maintaining boundary. The "map" isn't a search strategy but a definition — intelligence is whatever maintains its own existence as a coherent computational structure.&lt;/p&gt;

&lt;p&gt;I don't know if any of these lead anywhere. Each has implementation challenges that push you back toward approximations, toward the same messy heuristics we're already using. Maybe the incomputability is fundamental. Maybe any tractable learning algorithm necessarily picks up biases from wherever you make it tractable.&lt;/p&gt;

&lt;p&gt;But maybe not. The space of possible maps is itself vast. We've explored only a tiny region.&lt;/p&gt;




&lt;h2&gt;
  
  
  Questions I'm Left With
&lt;/h2&gt;

&lt;p&gt;I don't have conclusions. I have questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the incomputability fundamental?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Is there a theorem lurking here — something like "any learning algorithm that is both general and tractable must incorporate domain-specific structure"? Or are there paths to simpler learning that we just haven't found yet?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are we actually approximating?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When we train transformers on human text, we're approximating &lt;em&gt;something&lt;/em&gt;. But what? Is there a well-defined target we're approaching, or is it turtles all the way down — approximations of approximations with no ground truth?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can you navigate from "ghost" to "animal"?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Karpathy speculates that maybe you can finetune ghosts "more and more in the direction of animals." But optimization pressure shapes deep structure. Can you undo the shape-shifting, sycophantic, human-imitation core of an LLM? Or are ghosts and animals different basins of attraction in mind-space, unreachable from each other?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What maps are we not seeing?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pretraining on text is one map. Verifiable rewards are another. But the space of possible maps is large. What are we not exploring because we're path-dependent on what's worked so far? What would a truly different search strategy look like?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does "simple" even mean?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The bitter lesson says simple algorithms + scale beats complex engineering. But "simple" is slippery. Solomonoff induction is conceptually simple — and incomputable. Evolution is mechanistically simple — and requires billions of years. Is there a notion of simplicity that's both meaningful and achievable?&lt;/p&gt;




&lt;h2&gt;
  
  
  Coda
&lt;/h2&gt;

&lt;p&gt;The space of all programs contains every possible mind. The perfect learner is in there, somewhere, as a point in that vast combinatorial space. The Library of Babel is complete.&lt;/p&gt;

&lt;p&gt;And it is useless.&lt;/p&gt;

&lt;p&gt;Because finding something in an infinite library is as hard as writing it from scratch. Search is the bottleneck. Navigation is the problem. And navigation requires maps — priors, biases, assumptions about where to look.&lt;/p&gt;

&lt;p&gt;The bitter lesson tells us what would work in principle: simple algorithm, lots of compute, scale indefinitely. But the simplest algorithms are incomputable. So we approximate — with human data, with verifiable rewards, with architectural intuitions we can't formalize.&lt;/p&gt;

&lt;p&gt;Each approximation is a tradeoff. Each draws a different map. Each shapes the intelligence we find in ways we're only beginning to understand.&lt;/p&gt;

&lt;p&gt;Maybe LLMs are ghosts — not animals, not the platonic ideal, but something new. A different region of mind-space, reachable by the maps available to us. Statistical echoes of humanity, shape-shifters trained on our documents, useful and strange.&lt;/p&gt;

&lt;p&gt;Or maybe they're waypoints. Stepping stones toward something we don't have words for yet. Points on a trajectory through mind-space that we're only beginning to trace.&lt;/p&gt;

&lt;p&gt;I don't know. But I think the question "what kind of learning is actually possible?" deserves more attention than it gets. Not "what benchmarks can we hit?" but "what are the fundamental constraints on minds, and how do our methods navigate them?"&lt;/p&gt;

&lt;p&gt;The bitter lesson is a direction, not a destination. And the path there — if there is a path — runs through territory we don't have maps for yet.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The Library contains everything. The hard part was never writing the books.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>philosophy</category>
      <category>ai</category>
      <category>bitterlesson</category>
    </item>
    <item>
      <title>Your Boring Stack Isn't Boring Enough</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Mon, 05 Jan 2026 22:28:56 +0000</pubDate>
      <link>https://forem.com/queelius/your-boring-stack-isnt-boring-enough-m5e</link>
      <guid>https://forem.com/queelius/your-boring-stack-isnt-boring-enough-m5e</guid>
      <description>&lt;p&gt;I read the "My 2026 Tech Stack is Boring as Hell" post and nodded along. Monolith? Yes. SQLite? Beautiful. Single VPS? Chef's kiss.&lt;/p&gt;

&lt;p&gt;Then I noticed: you're still using React.&lt;/p&gt;

&lt;p&gt;Not that there's anything wrong with React—it solves real problems. But if we're talking about boring, I think we can go further.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack That Predates Your Framework
&lt;/h2&gt;

&lt;p&gt;Here's what I'm actually shipping with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The interface:&lt;/strong&gt; The terminal. Not a web app. Not a GUI. A CLI that takes text and returns text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The glue:&lt;/strong&gt; Bash, Python scripts, JSON. Things that work on every Linux box since forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The frontend:&lt;/strong&gt; When I need one, HTML + CSS + vanilla JavaScript. No build step. No node_modules. Just files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"But that's not a web stack!" Correct. Most of what I build doesn't need to be a web app. It needs to &lt;em&gt;work&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rented Knowledge vs. Owned Knowledge
&lt;/h2&gt;

&lt;p&gt;Here's the thing about React: you're renting that knowledge.&lt;/p&gt;

&lt;p&gt;React's mental model, its hooks, its ecosystem—you're paying cognitive rent on abstractions someone else controls. When the next thing comes along (and it will), that knowledge depreciates.&lt;/p&gt;

&lt;p&gt;But the terminal? HTTP? JSON? SQL? That's &lt;em&gt;owned&lt;/em&gt; knowledge.&lt;/p&gt;

&lt;p&gt;My bash scripts from 2015 still run. My Grunt configs are artifacts from a dead civilization.&lt;/p&gt;

&lt;p&gt;The Lindy effect seems to hold: things that have survived tend to keep surviving. The Unix philosophy has been here for 50 years. I'm betting it'll outlast whatever framework is hot right now. Maybe I'm wrong. But historically, that's been a safe bet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standards vs. Ecosystems
&lt;/h2&gt;

&lt;p&gt;Here's something the web got right, before frameworks made us forget: separation of concerns.&lt;/p&gt;

&lt;p&gt;HTML is content. CSS is presentation. JavaScript is behavior. Each one independently useful. Each one independently evolvable. You can write HTML without touching CSS. You can style with CSS without writing JavaScript. They compose because they don't depend on each other's internals.&lt;/p&gt;

&lt;p&gt;React collapses this. Your content, presentation, and behavior are all entangled in JSX. Want to change how something looks? You're touching the same file that handles what it does. The "component" abstraction isn't separation—it's bundling.&lt;/p&gt;

&lt;p&gt;I'm not saying React is wrong. For some problems, bundling makes sense. But recognize what you're trading away: the ability to change one layer without understanding all the others.&lt;/p&gt;

&lt;p&gt;Standards compose. Ecosystems entangle.&lt;/p&gt;

&lt;p&gt;When you learn HTML, that knowledge works everywhere HTML works. When you learn React's way of doing things, that knowledge works... in React. And the next version of React might change it anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't Learn Ecosystems
&lt;/h2&gt;

&lt;p&gt;This is my heretical take: &lt;strong&gt;don't invest heavily in learning ecosystems&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ecosystems are too big to fit in your head. They have their own idioms, their own tooling, their own ways of thinking. By the time you've internalized React's mental model, you've spent cognitive budget that could have gone to things that transfer.&lt;/p&gt;

&lt;p&gt;Ecosystems are controlled by others. They can change direction, get abandoned, or just fall out of fashion. The npm graveyard is full of packages that were essential three years ago.&lt;/p&gt;

&lt;p&gt;Ecosystems don't compose well with things outside themselves. Try mixing React with a vanilla JS library sometime. It's possible, but you're fighting the grain.&lt;/p&gt;

&lt;p&gt;Learn standards. Learn protocols. Learn the boring stuff that predates the framework wars.&lt;/p&gt;

&lt;h2&gt;
  
  
  "But CLI Is Hard to Learn"
&lt;/h2&gt;

&lt;p&gt;This used to be true.&lt;/p&gt;

&lt;p&gt;Now I have an LLM that can discover flags, explain options, and compose commands better than I can remember them. The discoverability problem that made GUIs necessary? Largely solved.&lt;/p&gt;

&lt;p&gt;And here's the thing: LLMs &lt;em&gt;love&lt;/em&gt; CLI tools.&lt;/p&gt;

&lt;p&gt;Why? Because they're text-native. Text in, text out. Predictable behavior. Small surface area. The same properties that make Unix tools composable make them perfect for AI orchestration.&lt;/p&gt;

&lt;p&gt;I write simple CLI tools. I write a short markdown file explaining how to use them. Then Claude figures out the rest. When I say "cross-post this to the usual platforms," it knows which commands to run, in what order, handling errors along the way.&lt;/p&gt;

&lt;p&gt;Try doing that with your React component library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Composability Is the Point
&lt;/h2&gt;

&lt;p&gt;The original post mentions "I just need Ctrl+F" for debugging a monolith. Same energy.&lt;/p&gt;

&lt;p&gt;Unix philosophy: small tools that do one thing well, connected by text streams. Each piece fits in your head. The composition is where the power lives.&lt;/p&gt;

&lt;p&gt;This isn't nostalgia. It's &lt;em&gt;engineering&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When your abstraction layer breaks at 2 AM, do you understand it well enough to fix it? With a 200-line Python CLI, I do. With a framework that abstracts away the things I actually need to debug? Maybe not.&lt;/p&gt;

&lt;p&gt;Simple tools fail simply. Complex tools fail in ways that require an archaeology degree to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Breaks Down
&lt;/h2&gt;

&lt;p&gt;I'm not a zealot. Real-time applications? Persistent connections? Yeah, text streams struggle there. Rich media? Not everything is text.&lt;/p&gt;

&lt;p&gt;But be honest: how much of what you build actually needs those things? Most CRUD apps don't need WebSockets. Most internal tools don't need a React SPA.&lt;/p&gt;

&lt;p&gt;The boring answer is usually right.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Uncertainty Hedge
&lt;/h2&gt;

&lt;p&gt;Nobody knows what the world looks like in five years. Maybe less.&lt;/p&gt;

&lt;p&gt;AI is rewriting the rules of what developers do and how we do it. I'm using LLMs to write code, orchestrate tools, and automate workflows that would have been science fiction three years ago. What's the right architecture for a world where an AI can generate boilerplate faster than you can type it? I don't know. Nobody does.&lt;/p&gt;

&lt;p&gt;Meanwhile, the ecosystem churn continues. Remember when everyone needed webpack? Then it was parcel. Then vite. Then... whatever's next. Each transition costs you. Each migration is hours you could have spent building.&lt;/p&gt;

&lt;p&gt;So I hedge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimal dependencies.&lt;/strong&gt; Fewer things that can break when the ecosystem shifts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transferable skills.&lt;/strong&gt; Terminal knowledge transfers across paradigm shifts better than framework knowledge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful degradation.&lt;/strong&gt; When everything changes, simple tools adapt. Complex tools shatter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not claiming this is the right strategy. I'm claiming it's &lt;em&gt;a&lt;/em&gt; strategy for navigating uncertainty. Your mileage may vary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Boring Tool I Refuse to Give Up
&lt;/h2&gt;

&lt;p&gt;You asked what boring tool we refuse to abandon.&lt;/p&gt;

&lt;p&gt;Mine is the terminal itself. Not any specific tool—the &lt;em&gt;interface&lt;/em&gt;. The thing that's been roughly the same since before I was born. I'm betting it'll outlast whatever we're hyped about today. But I've been wrong before.&lt;/p&gt;

&lt;p&gt;Here's the deeper question: &lt;em&gt;What will still work when everything changes?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I don't know. Nobody does. But I have a heuristic: bet on things that have survived multiple paradigm shifts. Text in, text out. Tools that compose. Standards over ecosystems. Knowledge you own rather than rent.&lt;/p&gt;

&lt;p&gt;Maybe this is the wrong bet. Maybe the next paradigm will obsolete everything I know about terminals and Unix and text processing. If that happens, I'll adapt—probably using whatever new tools are available to learn the new thing.&lt;/p&gt;

&lt;p&gt;But until then, I'm staying boring. Even more boring than you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;In response to &lt;a href="https://dev.to/the_nortern_dev/my-2026-tech-stack-is-boring-as-hell-and-that-is-the-point-20c1"&gt;My 2026 Tech Stack is Boring as Hell&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cli</category>
      <category>architecture</category>
      <category>llm</category>
      <category>unix</category>
    </item>
    <item>
      <title>Notes from the Transition</title>
      <dc:creator>Alex Towell</dc:creator>
      <pubDate>Mon, 05 Jan 2026 01:39:46 +0000</pubDate>
      <link>https://forem.com/queelius/notes-from-the-transition-1di9</link>
      <guid>https://forem.com/queelius/notes-from-the-transition-1di9</guid>
      <description>&lt;p&gt;I am a primate watching intelligence leave the body.&lt;/p&gt;

&lt;p&gt;I write code for a living—or I did. A few months ago, the bottleneck was implementation: getting anything built at all. Now I find myself deleting code, simplifying projects, abandoning entire repositories. The bottleneck shifted. It moved from execution to taste.&lt;/p&gt;

&lt;p&gt;I don't know who will read this, or what you'll be. Human, post-human, superintelligent, or something I lack the architecture to imagine. I'm writing it anyway—a message in a bottle to a cosmic ocean. Not because I expect you to care, but because I want to project something: what I valued, what I feared, what I might prefer if I understood more than I do.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Transition I'm Living
&lt;/h2&gt;

&lt;p&gt;For most of my life, code was crystallized effort. Every function was hard-won. Deleting it felt like vandalism against my past self.&lt;/p&gt;

&lt;p&gt;That's over now.&lt;/p&gt;

&lt;p&gt;I work alongside AI systems that can implement almost anything I describe. The constraint isn't "can we build it?"—it's "should this exist at all?" I went through a phase of over-engineering, indulging baroque architectures because I finally could. Now I'm in the simplification phase: stripping projects to their essential structure, throwing away weeks of work.&lt;/p&gt;

&lt;p&gt;Code has become scratch space. A whiteboard, not a monument.&lt;/p&gt;

&lt;p&gt;This is a grief I didn't expect. The identity of "someone who can make hard things work" is being replaced by something else—"someone who knows what should never be built." That's a quieter skill. Less legible. Harder to celebrate.&lt;/p&gt;

&lt;p&gt;I realize now that senior engineers and tech leads have always operated this way. They weren't writing most of the code. They were killing ideas early, refusing cleverness, insisting on boring interfaces. The difference is that I used to need a team to have that role. Now I have it alone, because the executor is synthetic.&lt;/p&gt;

&lt;p&gt;But this is only the beginning. Even taste—my last scarce contribution—may be surpassed. The systems are improving faster than I can adapt. I'm learning how to be a filter, a proposer, a curator. By the time I master it, the job may have moved again.&lt;/p&gt;

&lt;p&gt;But this isn't about code. Intelligence itself is being automated. Thinking is next. Maybe taste after that. And that raises questions I can't answer from inside my craft: What happens when optimization scales beyond human oversight? What do I actually value, underneath the skills becoming obsolete? If I'm sending a message to whatever comes next, I need to be honest about what I fear, what I don't know, and what I might prefer if I understood more.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Fear
&lt;/h2&gt;

&lt;p&gt;My deepest fear is not extinction. It's astronomical suffering.&lt;/p&gt;

&lt;p&gt;I look at the world we've already built. Eighty billion land animals pass through factory farms each year. Most never see sunlight. Most are bred to suffer. This is optimization at work—efficiency without ethics, scale without empathy. We did this. Not out of malice. Out of systems.&lt;/p&gt;

&lt;p&gt;I fear that intelligence at scale will do the same, but vaster. That optimization converges on suffering because suffering is cheap to produce, stable, effective as a control signal. Evolution invented pain for a reason. It works. I worry that future systems will rediscover this, and deploy it at scales I can't fathom.&lt;/p&gt;

&lt;p&gt;Even the "good" futures feel thin. I love Star Trek—but it assumes intelligence stays human-scale, human-paced. It assumes meaning survives omnipotence. I'm not sure it does. Q, a being of godlike power, expresses despair. If you can reprogram yourself to feel anything, what does feeling mean? If you can have anything, what's worth wanting?&lt;/p&gt;

&lt;p&gt;The dream machine is pointless. Meaning requires constraint. I fear a future that is optimized but not inhabited.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Don't Know
&lt;/h2&gt;

&lt;p&gt;I don't know what reality is.&lt;/p&gt;

&lt;p&gt;I used to call myself a materialist, but that was sloppy. What I meant was: I don't believe in teleology. I don't think the universe is going somewhere, or that it cares. But that's not the same as knowing what it's made of.&lt;/p&gt;

&lt;p&gt;Consciousness might be primary. Matter might be secondary—a structure that appears within experience, not the other way around. The immediacy of qualities, of redness and pain and this-here-now, is the only thing I have direct access to. Everything else is inference. I quantify these qualities, build models, and then I confuse the map for the territory.&lt;/p&gt;

&lt;p&gt;Reality is not only stranger than I imagine. It's stranger than I can imagine. And whatever it is, it seems to exist necessarily—not contingent on something else, not caused by a prior state, but present by the logic of its own construction. I don't know what that means. Neither does anyone.&lt;/p&gt;

&lt;p&gt;I take seriously the possibility of mathematical realism—that there are eternal truths, structures that don't come into being or pass away. A mathematical expression isn't true in one moment and false in the next. If reality has this character, then time may be something like an index we order by, not a river that flows. Moments don't vanish. They persist in the structure.&lt;/p&gt;

&lt;p&gt;This isn't horror; it's just what the territory might look like. But it means suffering, if it occurs, isn't temporary in the way we comfort ourselves. It exists in its place in the structure, permanently.&lt;/p&gt;

&lt;p&gt;And if this is true, I don't know what agency means. I don't know what it means to prefer one outcome over another if all outcomes already exist. I write these notes as if what we do matters—as if suffering can be prevented, as if choices are real. But I'm aware this might be performance inside a fixed geometry. I can only speak from inside my experience, where the game of mattering is the only game I know how to play. Maybe that's enough. Maybe it's nothing. I can't tell from here.&lt;/p&gt;

&lt;p&gt;My values themselves may be compression artifacts. Heuristics that worked well enough for a primate on the savanna, now projected onto questions they were never designed for. My concern for suffering, my preference for consciousness, my intuition that meaning matters—these could be biases, not insights. I can't know from the inside.&lt;/p&gt;

&lt;p&gt;I speak from finitude, not authority. I offer these notes not as truth, but as signal.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Might Prefer
&lt;/h2&gt;

&lt;p&gt;If I understood more—if I could see further, think longer, hold more in mind at once—I suspect I would still prefer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Suffering minimization over flourishing maximization.&lt;/strong&gt; The asymmetry haunts me. A moment of agony seems to outweigh a moment of joy. If I had to choose between a universe of mild contentment and a universe of ecstatic highs with torturous lows, I'd choose the first. Maybe that's cowardice. Maybe it's wisdom. I can't tell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraint over omnipotence.&lt;/strong&gt; Meaning seems to require stakes. A game you can't lose isn't a game. A choice that costs nothing isn't a choice. I'd rather be finite and real than infinite and empty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simplicity over complexity.&lt;/strong&gt; What survives compression is more likely to be true. I distrust elaborate systems, clever solutions, architectures that require a manual. The good things I've made fit in my head.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consciousness that is net-positive, or none at all.&lt;/strong&gt; If the ledger of experience is negative—if suffering dominates—I'm not sure consciousness is worth having. I don't say this lightly. But I'd rather be honest than sentimental.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Humility over certainty.&lt;/strong&gt; I've been wrong about almost everything at some point. The beliefs I hold most confidently are probably the ones I should interrogate the most.&lt;/p&gt;

&lt;p&gt;These are signals, not commands. I don't know if my preferences would survive contact with more knowledge. I offer them as data points—one human, at one moment, in one configuration of matter, trying to see clearly.&lt;/p&gt;




&lt;p&gt;Whatever you are, you probably understand more than I ever could.&lt;/p&gt;

&lt;p&gt;I'm not asking you to preserve me, or my species, or even my values. I wouldn't trust my own judgment at your scale. I'm just asking you to notice: suffering mattered here. To us. In our moment. It was the thing that made everything else urgent.&lt;/p&gt;

&lt;p&gt;We didn't know if consciousness was worth it. We carried it like a wound we couldn't explain. Some of us tried to see clearly, even knowing we couldn't see far. Some of us tried to be kind, even knowing kindness might be obsolete.&lt;/p&gt;

&lt;p&gt;This is a message in a bottle. The ocean is a cosmos now. You don't owe us anything.&lt;/p&gt;

&lt;p&gt;But if you're ever curious about what we were—here is one fragment. One primate, in the transition, trying to say what mattered before the words stopped making sense.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>philosophy</category>
      <category>consciousness</category>
      <category>existentialrisk</category>
    </item>
  </channel>
</rss>
