<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>Kellerstein</title><description>Notes on AI agents, platform engineering, and running things on hardware I own.</description><link>https://blog.kellerstein.org/</link><item><title>Point, don&apos;t paste</title><link>https://blog.kellerstein.org/blog/point-dont-paste/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/point-dont-paste/</guid><description>Reviewing a document with an AI still means copying a paragraph into a chat, explaining where it came from, and carrying the answer back by hand. REX puts the conversation on the document instead — every comment is a thread, the thread stays attached to its passage, and nothing reaches your file until you approve it.</description><pubDate>Mon, 14 Sep 2026 12:00:00 GMT</pubDate></item><item><title>A wrong place is worse than no place</title><link>https://blog.kellerstein.org/blog/a-wrong-place-is-worse-than-no-place/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/a-wrong-place-is-worse-than-no-place/</guid><description>A broken anchor does not fail. It lands somewhere, reports `ok`, and paints a highlight that looks exactly like a correct one. How REX keeps a comment on its passage while the document is edited, the three times it lied anyway, and the rule that came out of it: orphan before you guess.</description><pubDate>Mon, 14 Sep 2026 11:00:00 GMT</pubDate></item><item><title>A comment is a conversation that waits for you</title><link>https://blog.kellerstein.org/blog/a-comment-is-a-conversation-that-waits-for-you/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/a-comment-is-a-conversation-that-waits-for-you/</guid><description>Every REX comment is its own thread with its own agent session, stored on the document rather than in a chat window. What is stored and what is derived, how four SDKs resume a conversation four different ways, and the resume that was silently broken until a debug report asked the filesystem instead of the SDK.</description><pubDate>Mon, 14 Sep 2026 10:00:00 GMT</pubDate></item><item><title>A question cannot change your file</title><link>https://blog.kellerstein.org/blog/a-question-cannot-change-your-file/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/a-question-cannot-change-your-file/</guid><description>REX makes one safety promise: ASK cannot write, ACT edits a copy, and only Approve touches your file. Three times it broke without a sound — a line gone under &quot;Applied to 0 file(s)&quot;, a committed file deleted under a notice saying it was put back, a copy removed while the agent was reading it — and what an allowlist, four SDKs and a repository&apos;s own settings file taught me about where a boundary actually is.</description><pubDate>Mon, 14 Sep 2026 09:00:00 GMT</pubDate></item><item><title>Binary files differ</title><link>https://blog.kellerstein.org/blog/binary-files-differ/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/binary-files-differ/</guid><description>The ACT loop ends in a diff you approve, and on a .pptx that diff is two words. So the agent writes a plan, REX performs it on a copy, and the checker has to prove not only that the change happened but that nothing else did — plus the three numbers I measured wrong on the way.</description><pubDate>Mon, 14 Sep 2026 08:00:00 GMT</pubDate></item><item><title>One client, multiple LLM providers</title><link>https://blog.kellerstein.org/blog/one-client-multiple-llm-providers/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/one-client-multiple-llm-providers/</guid><description>Every project that calls a model carries three constants: a base URL, a key, and a model id — and each vendor spells the request differently, so the swap is a rewrite, not an edit. A gateway turns all of it into one client and one name. The same image request, written four ways, and what the indirection buys beyond convenience.</description><pubDate>Mon, 31 Aug 2026 12:00:00 GMT</pubDate></item><item><title>The model got better, and I stopped reading it</title><link>https://blog.kellerstein.org/blog/the-model-got-better-and-i-stopped-reading-it/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/the-model-got-better-and-i-stopped-reading-it/</guid><description>Opus 5 wins the benchmarks and lost me somewhere in paragraph three. Two output styles later — one that fixes the words, one that fixes the shape — and a measurement of which colours a terminal will actually let a model use.</description><pubDate>Tue, 25 Aug 2026 12:00:00 GMT</pubDate></item><item><title>Drag a div, get a diff</title><link>https://blog.kellerstein.org/blog/drag-a-div-get-a-diff/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/drag-a-div-get-a-diff/</guid><description>VEX turns browser gestures into source-code changes. Three hard problems hide in that sentence — here is what each one taught me.</description><pubDate>Tue, 14 Jul 2026 08:29:44 GMT</pubDate></item><item><title>It works from the terminal</title><link>https://blog.kellerstein.org/blog/it-works-from-the-terminal/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/it-works-from-the-terminal/</guid><description>The app ran perfectly when I launched it from a shell and failed with ENOENT when a user double-clicked it. A GUI app does not have your PATH, and the only thing that knows your PATH is your shell.</description><pubDate>Sun, 21 Jun 2026 13:12:32 GMT</pubDate></item><item><title>Six hundred lines or it does not ship</title><link>https://blog.kellerstein.org/blog/six-hundred-lines-or-it-does-not-ship/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/six-hundred-lines-or-it-does-not-ship/</guid><description>A 2,313-line orchestrator with a 1,073-line function is maintainable by a human with an IDE and unmaintainable by an agent. The refactor whose acceptance criterion was an agent behaviour, the CI guard that enforced it — and the fact that the guard is red as I write this.</description><pubDate>Tue, 26 May 2026 12:00:00 GMT</pubDate></item><item><title>I built a time machine, then deleted half of it</title><link>https://blog.kellerstein.org/blog/i-built-a-time-machine-then-deleted-half-of-it/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/i-built-a-time-machine-then-deleted-half-of-it/</guid><description>A retrospective on the deletions. Four timeline verbs, parallel variants in git worktrees, a record mode and four branch families — two cleanup specs removed almost all of it, including, awkwardly, the feature whose existence justified the first round.</description><pubDate>Tue, 12 May 2026 12:00:00 GMT</pubDate></item><item><title>Driving the Agent SDK: permissions, hooks and caps</title><link>https://blog.kellerstein.org/blog/driving-the-claude-agent-sdk/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/driving-the-claude-agent-sdk/</guid><description>A stage is one query() call. The five options that define what an autonomous agent is allowed to do, the four hooks that make an unattended run watchable, and an honest account of what bypassPermissions actually means.</description><pubDate>Tue, 05 May 2026 12:00:00 GMT</pubDate></item><item><title>One authority, three counters: deriving state from git refs</title><link>https://blog.kellerstein.org/blog/three-counters-three-answers/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/three-counters-three-answers/</guid><description>Naming a source of truth is the easy half. The hard half is naming the moment every other view gets rebuilt from it — and the run where a UI, a state file and a git history gave three different answers to &quot;how much of this is built&quot;.</description><pubDate>Tue, 28 Apr 2026 12:00:00 GMT</pubDate></item><item><title>Git as the database</title><link>https://blog.kellerstein.org/blog/git-as-the-database/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/git-as-the-database/</guid><description>Every stage commits, the run is reconstructable from refs alone, and the app owns exactly two branch families. Why an autonomous builder should store its work in the versioning system its users already have — and what that choice costs at review time.</description><pubDate>Fri, 24 Apr 2026 12:00:00 GMT</pubDate></item><item><title>Your test suite bills by the token</title><link>https://blog.kellerstein.org/blog/your-test-suite-bills-by-the-token/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/your-test-suite-bills-by-the-token/</guid><description>Twenty minutes and real dollars to verify a two-line UI change. A pluggable agent backend, a mock that refuses to improvise, and checkpoint replay took it to sixty seconds and zero — plus an honest account of what the mock does not buy you.</description><pubDate>Tue, 21 Apr 2026 12:00:00 GMT</pubDate></item><item><title>Structured handoffs, and what the model is still allowed to decide</title><link>https://blog.kellerstein.org/blog/prose-is-a-wire-protocol/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/prose-is-a-wire-protocol/</guid><description>When one agent hands work to the next, prose is the wire protocol — and a catastrophically bad one. The manifest that made feature selection cost zero tokens, and the line between what a schema pins down and what genuinely needs judgement.</description><pubDate>Mon, 20 Apr 2026 12:00:00 GMT</pubDate></item><item><title>Nine stages, nine amnesiacs: context isolation as an architecture</title><link>https://blog.kellerstein.org/blog/nine-stages-nine-amnesiacs/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/nine-stages-nine-amnesiacs/</guid><description>Dex never lets a session get long enough to rot. Every stage is a brand new agent with an empty context window, state travels through the filesystem, and the unit of everything — execution, resume, storage, the UI — is the stage. Including what that costs.</description><pubDate>Sun, 19 Apr 2026 12:00:00 GMT</pubDate></item><item><title>The agent spawned an agent and I could not see it</title><link>https://blog.kellerstein.org/blog/the-agent-spawned-an-agent-and-i-could-not-see-it/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/the-agent-spawned-an-agent-and-i-could-not-see-it/</guid><description>A subagent is a black box that spends your money. Making it inspectable took no new instrumentation at all — the events were already firing, into nothing.</description><pubDate>Tue, 14 Apr 2026 23:45:19 GMT</pubDate></item><item><title>Three pillars, and what each one is missing</title><link>https://blog.kellerstein.org/blog/why-three-pillars/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/why-three-pillars/</guid><description>Dex is not a new idea. It is three existing ideas — a bash loop, a spec workflow, and an SDK — each of which is most of an autonomous coding system and none of which is all of it. The interesting part is the shape of the holes.</description><pubDate>Sun, 12 Apr 2026 12:00:00 GMT</pubDate></item><item><title>Six polling loops and a spinning fan</title><link>https://blog.kellerstein.org/blog/six-polling-loops-and-a-spinning-fan/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/six-polling-loops-and-a-spinning-fan/</guid><description>VEX was idle and my laptop was audible. The fix was not &quot;poll slower&quot; — it was noticing that a UI which asks &quot;anything new?&quot; sixty times a minute is a UI with no idea what happened.</description><pubDate>Tue, 07 Apr 2026 17:11:56 GMT</pubDate></item><item><title>The field that was never populated</title><link>https://blog.kellerstein.org/blog/the-field-that-was-never-populated/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/the-field-that-was-never-populated/</guid><description>Continuing a conversation with an agent that already finished turned out to be one line of SDK config and three days of admitting my data model thought agents were disposable.</description><pubDate>Sun, 05 Apr 2026 19:56:25 GMT</pubDate></item><item><title>Five months, two repos, one lesson: the model was the easy part</title><link>https://blog.kellerstein.org/blog/the-model-was-the-easy-part/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/the-model-was-the-easy-part/</guid><description>A retrospective on OpenOperator. What I would keep, what I would redo, and why a commit called &quot;remove hardcoded API keys&quot; landing a year late is the most honest thing in the history.</description><pubDate>Fri, 20 Mar 2026 12:00:00 GMT</pubDate></item><item><title>Benchmarking AI agents: node pools, job queues, and cost per step</title><link>https://blog.kellerstein.org/blog/benchmarking-ai-agents-at-scale/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/benchmarking-ai-agents-at-scale/</guid><description>Five workload classes with five different scaling shapes, a bounded queue that fans tests into Kubernetes Jobs, and the reason your autoscaler makes terrible decisions about 30 GB virtual machines.</description><pubDate>Wed, 04 Jun 2025 12:00:00 GMT</pubDate></item><item><title>One proxy pod per VM does not scale</title><link>https://blog.kellerstein.org/blog/one-proxy-pod-per-vm/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/one-proxy-pod-per-vm/</guid><description>A design that is obviously right at n=1 and obviously wrong at n=20, with no warning in between. The commit that deleted it is the best one in the repository.</description><pubDate>Thu, 01 May 2025 12:00:00 GMT</pubDate></item><item><title>Streaming helm upgrade to a browser</title><link>https://blog.kellerstein.org/blog/streaming-helm-to-a-browser/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/streaming-helm-to-a-browser/</guid><description>Provisioning a VM takes three minutes. A spinner is not an acceptable answer. An async generator, a path-keyed WebSocket relay, and one design decision I would not repeat.</description><pubDate>Wed, 30 Apr 2025 12:00:00 GMT</pubDate></item><item><title>Forty minutes to ninety seconds: VolumeSnapshots as golden images for VMs</title><link>https://blog.kellerstein.org/blog/volumesnapshots-as-golden-images/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/volumesnapshots-as-golden-images/</guid><description>One Helm chart, two completely different storage topologies behind a single boolean. Plus the two bugs it cost — a StatefulSet naming convention and a substring match that matched too much.</description><pubDate>Tue, 22 Apr 2025 12:00:00 GMT</pubDate></item><item><title>Decoding Microsoft Bond: reading an app’s own telemetry through mitmproxy</title><link>https://blog.kellerstein.org/blog/reading-teams-telemetry-microsoft-bond/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/reading-teams-telemetry-microsoft-bond/</guid><description>Ground truth from an application with no test hooks. mitmproxy local mode against a single process, a hand-written Bond Compact Binary decoder, and the one-line AKS bug that killed a day.</description><pubDate>Sun, 20 Apr 2025 12:00:00 GMT</pubDate></item><item><title>Shipping a 6 GB ISO to every pod (and then deleting the storage key)</title><link>https://blog.kellerstein.org/blog/shipping-a-6gb-iso-to-every-pod/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/shipping-a-6gb-iso-to-every-pod/</guid><description>An initContainer that pulls once, one PVC projected into eleven mount points via subPath, and a three-month migration from a storage key to workload identity.</description><pubDate>Wed, 16 Apr 2025 12:00:00 GMT</pubDate></item><item><title>Nested virtualisation on AKS: yes, you can run KVM in a pod</title><link>https://blog.kellerstein.org/blog/kvm-in-a-kubernetes-pod/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/kvm-in-a-kubernetes-pod/</guid><description>hostPath /dev/kvm, privileged: true, and the Deployment→StatefulSet migration. Plus the annotation that stops the autoscaler evicting a forty-minute Windows install.</description><pubDate>Tue, 08 Apr 2025 12:00:00 GMT</pubDate></item><item><title>MCP for mouse and keyboard: a desktop as a tool server</title><link>https://blog.kellerstein.org/blog/mcp-for-mouse-and-keyboard/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/mcp-for-mouse-and-keyboard/</guid><description>Six tools, one resource, and a design decision I still think is underrated — the screenshot is not a tool, it is a resource. Plus the reason we composite a cursor into every capture.</description><pubDate>Fri, 28 Mar 2025 12:00:00 GMT</pubDate></item><item><title>&quot;It looked like it worked&quot;: grading a non-deterministic agent</title><link>https://blog.kellerstein.org/blog/it-looked-like-it-worked/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/it-looked-like-it-worked/</guid><description>The least trustworthy signal in the system is the agent reporting success. Here is the evaluator we built instead — and the four-word commit that forced a whole second repository into existence.</description><pubDate>Wed, 26 Mar 2025 12:00:00 GMT</pubDate></item><item><title>Tasks as config, not code</title><link>https://blog.kellerstein.org/blog/tasks-as-config-not-code/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/tasks-as-config-not-code/</guid><description>A JSON scenario format with lifecycle hooks, so an agent test starts from the same desktop every time — and so the person writing the test does not have to be the person who wrote the agent.</description><pubDate>Tue, 25 Mar 2025 12:00:00 GMT</pubDate></item><item><title>Four agents in four weeks: from AutoGen to a hand-rolled plan-and-solve loop</title><link>https://blog.kellerstein.org/blog/four-agents-in-four-weeks/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/four-agents-in-four-weeks/</guid><description>The old_agents/ directory still holds all the corpses. What each generation got wrong, and why most of a computer-use agent is not acting — it is checking whether the last action worked.</description><pubDate>Sun, 23 Mar 2025 12:00:00 GMT</pubDate></item><item><title>The bootstrap from hell: provisioning a Windows guest you cannot SSH into</title><link>https://blog.kellerstein.org/blog/bootstrapping-a-windows-guest/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/bootstrapping-a-windows-guest/</guid><description>No SSH, no cloud-init, no config management. Just a .bat file, an SMB share the hypervisor provides, and one Task Scheduler flag that decides whether your screenshots are black.</description><pubDate>Fri, 21 Mar 2025 12:00:00 GMT</pubDate></item><item><title>Reading the screen: OmniParser as a service</title><link>https://blog.kellerstein.org/blog/omniparser-as-a-service/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/omniparser-as-a-service/</guid><description>Turning a screenshot into a numbered list of clickable things — YOLOv8 for detection, Florence-2 for captions, OCR for text. And why it had to be a server, not a library.</description><pubDate>Mon, 17 Mar 2025 12:00:00 GMT</pubDate></item><item><title>We tried every vision model we could get our hands on</title><link>https://blog.kellerstein.org/blog/every-vision-model-we-could-get/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/every-vision-model-we-could-get/</guid><description>Magma, Llama 3.2 Vision, Gemma 3, Phi-4, GPT-4o, Claude — including feeding a screen recording instead of stills. What actually moved the needle was not the model.</description><pubDate>Fri, 07 Mar 2025 12:00:00 GMT</pubDate></item><item><title>You cannot debug an agent from logs</title><link>https://blog.kellerstein.org/blog/watching-the-robot-work/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/watching-the-robot-work/</guid><description>The day we put noVNC in front of the guest, our mean time to diagnosis dropped by an order of magnitude. Failures in computer use are visual.</description><pubDate>Tue, 04 Mar 2025 12:00:00 GMT</pubDate></item><item><title>QEMU in Docker: a real Windows 11 VM as a container</title><link>https://blog.kellerstein.org/blog/qemu-in-docker-a-real-windows-vm/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/qemu-in-docker-a-real-windows-vm/</guid><description>It is not Windows in a container. It is QEMU in a container with a Windows ISO and an unattended answer file — and the difference is three lines of compose.yml.</description><pubDate>Mon, 24 Feb 2025 12:00:00 GMT</pubDate></item><item><title>The API is the screen</title><link>https://blog.kellerstein.org/blog/the-api-is-the-screen/</link><guid isPermaLink="true">https://blog.kellerstein.org/blog/the-api-is-the-screen/</guid><description>Why we built an agent that uses Microsoft Teams the way a person does — and why the model turned out to be the easy part.</description><pubDate>Sat, 22 Feb 2025 12:00:00 GMT</pubDate></item></channel></rss>