writing

Blog

Blog posts and whatever I'm tinkering with lately.

The OpenCode wordmark on a dark background, with the tagline "The open source AI coding agent".

OpenCode source code deep dive: how an open source agent harness really works

Aug 7, 2026·34 min read

One harness, taken apart subsystem by subsystem, in the order a request flows through it: the loop, the prompts, the tool surface, the permission system, the subagents, the context manager, and the undo. Every part is a file you can open, and most of them are smaller than you would guess.

aillmagentsopencodedeveloper-tools
A draft horse in full working harness, with the collar, blinders, reins and traces labelled as the model, context, permissions and tools.

What is an agent harness? Claude Code vs Codex CLI vs OpenCode

Aug 6, 2026·13 min read

A language model only turns text into text. The loop that lets it read files, run tests and fix its own mistakes is the harness, and it moves results as much as swapping models does. I cloned Claude Code's leaked prompts, Codex CLI and OpenCode to see what they actually do differently.

aillmagentsclaude-codedeveloper-tools
A diagram titled "Bandwidth is reads, not writes": one new message written to a 500-document messages table causes useQuery(listMessages) to re-run and re-read all 500 documents for every connected client, so 2 KB written becomes megabytes read.

Two users, tiny data: how my Convex chat app read tens of MB a day

Jun 13, 2026·5 min read

Convex bills you for reads, not storage. My chat app held a few hundred KB and still read tens of MB a day, because reactive queries re-read everything each time anything changed. Here's what was actually going on, and how I fixed it.

convexperformancedatabasereact
A two-panel diagram. "Always render": requestAnimationFrame, renderer.render() and GPU draws frame loop forever, producing 60 identical frames a second and a thermometer at the top. "Render on demand": a needsRender? gate sits in the loop, so only one frame is drawn and the thermometer stays low.

A motionless Three.js scene was cooking my phone

Jun 12, 2026·5 min read

My phone got hot showing a Ludo board that wasn't moving. The render loop was redrawing the same frame 60 times a second, and a dirty flag fixed it. Notes on render-on-demand, pixel ratio, and antialiasing on mobile.

three.jswebglperformancemobile
A two-lane diagram. "Programmatic insert": setState, JS updates value, native TextInput, then the layout pass is skipped, and the text runs straight off the right edge of the phone without wrapping. "The one-line fix": the same pipeline with setNativeProps added, the layout pass runs, and the text wraps onto three lines.

An iOS multiline TextInput that wouldn't wrap, and the one-line fix

Apr 20, 2026·4 min read

A mentions library inserts text through children instead of value, so iOS never re-runs layout and the input stops growing. One setNativeProps call fixes the wrap without a remount or keyboard flicker.

react-nativeiosdebugging