← Devlog

Convergent development

What I thought I was doing

The night Claude Fable 5 came out (for the first time, heh), I did what everyone did and stayed up too late pointing a new model at my hyper interesting fascination of the moment, something deep and profound and interesting.

In my case… a roguelike fishtank simulator.

Look, don’t judge me, ok?

Anyways, it went well, and I ground out the project way too late, lighting tokens on fire for about five hours. I bounced back and forth between Fable and Opus 4.8, trying to see what was producing better results, etc.

Sometime the next morning, after I’d come out of my fugue and had some coffee, I had a moment of slowly dawning horror as I looked between the five terminal windows I’d had open, juggling various projects at the same time.

Which terminal was logged into my work account, and which was logged into my personal? Did I just spend a big pile of company money on digital fish?

I was pretty sure I hadn’t, but I wasn’t positive, and for a panicked chunk of that morning before work I was poking through config directories, running claude --resume in different places, and hunting for receipts.

Turns out it was fine. Personal subscription the whole time… this time.

The issue here wasn’t accounting so much as it was the sharper edge of a duller problem, which was that as my AI development accelerated and let me work on more things at once, my setup had become genuinely chaotic. It was a problem, but it never quite moved above the attention bar where I needed to do something about it.

The reality is the whole mess was hung on spit and wires. I started only opening work projects in VS Code so that I could be sure I didn’t log into the wrong account, then tried using Zed for multiple projects at once, and still found myself wanting to run a few things in a terminal. Then I kept getting annoyed because Zed wouldn’t show me how much headroom I had left like Codex does at the terminal (how much of your context window is full), and the agents would just sometimes drop functionality, like --rc.

It’s not like you couldn’t run two setups alongside one another. Claude and Codex both let you do this using different home directories, but it’s just fiddly enough that I kept stumbling over things. It basically all works as long as you don’t forget your logins or run a new client or IDE that doesn’t support running the claude-personal shortcut you made, and of course I forgot constantly.

This is the actual reason LodeFlow exists, not because I saw some market opportunity to build a thing, but because I needed to fix my own setup. Once I sat down to actually build something for myself, though, I found a weird set of constraints and decisions cropping up.

The things I was not willing to give up

Once I finally decided to fix it, the questions were a lot less interesting than the fishtank had been, and they arrived more or less in this order.

Can I buy it? I looked. Not incredibly hard, in retrospect. I did a few searches and clicked around, but I was already pretty excited to build something and, well, you can probably guess how rigorous the procurement process was. I could find chat panels and terminals and tools built around one agent, but not much built around juggling the sessions themselves.

Can I mod it? This is the one I spent longest on, because it looked like the cheapest answer. Both editors had extension APIs. Both had a terminal. But the thing that was broken was the terminal hosting itself, and an extension was not going to repair its own host. I was not looking to add a panel. I needed the panel underneath to stop dropping my sessions.

So: how deep do I go, and what am I actually rewriting?

By the time I got there I had three things I was not willing to give up.

I wanted the agent’s own CLI. Plenty of tools rebuild the agent as a chat panel and plenty of people like them, but it was the wrong trade for me. Plan mode gets lost in the translation, slash commands go missing, the subagents and skills I configured are not there, and every time the vendor ships something new I have to wait for somebody else to wrap it. I wanted the strange little flags and rough edges because those are usually where the new capability shows up first. So I chose to host the real binary.

I wanted to stop watching the agents without becoming worse at reviewing them. One agent, I watch it. Two, I alt-tab. At five I was losing minutes at a time to the question of which window wanted something, and the worse failure was not the minutes. Constant low-grade triage makes me sloppy. When I am switching between five sessions and one throws a permission prompt, I approve it. I do not read it. That was exactly the habit I was trying to avoid, so I wanted session state without having to read every session’s output. At that point I was walking into a multiplexer through the back door whether I called it one or not.

I wanted the terminal to feel like a terminal. That meant escape-sequence handling faithful enough that a TUI written in 1994 and an agent written last month both render correctly, resize semantics that do not corrupt scrollback, selection, ligatures, wide glyphs, emoji, and enough speed that typing still feels attached to my hands. I could have taken a browser terminal and spent my time somewhere else. I did not want to start the project by accepting that trade, so I chose a native binary and inherited a surprisingly deep hole full of text rendering problems.

What are you willing to make your problem?

AI makes this a slightly dangerous question because it moves “maybe I could just build the stupid version” much closer to the top of the list.

I had recently watched Dave Plummer, who wrote the original Windows Task Manager, squeeze a fully featured Notepad into under 3 KB of pure assembly, which briefly made me wonder if I should write the terminal emulator in assembly. Luckily I did not. But for a few minutes “write the terminal emulator in assembly” was sitting next to “import alacritty_terminal” like these were two normal options in the decision tree.

I was perfectly happy to punt on enormous pieces of the stack. I did not write a GUI framework. I took iced, which is pre-1.0 and churns, and I pay for that churn every time it moves. I did not write a terminal emulator either; alacritty_terminal already existed and was much better at being a terminal emulator than anything I was going to produce on the way to building something else.

But two things in the repo are vendored and patched rather than imported: the terminal widget and the glyph atlas.

That was not a plan. It is where the bugs were.

On June 27, one word in a Markdown document rendered at the wrong size on every launch. The isolated section looked clean; let the rest of the document fill the atlas first and the word broke. We blamed atlas growth. Then initialization. Then the GPU upload. A 4,096-pixel atlas and an 8,192-pixel atlas produced byte-identical renders, which was awkward for the theory that atlas placement was the problem.

The CPU-side data was correct. The cache keys were correct. The evidence kept removing explanations without volunteering a replacement.

The root cause was a hinting cache in swash, two dependencies down. HintingCache::get() could reuse an LRU victim without updating its size and coordinate metadata, so one glyph got rasterized using facts about another. The full-app A/B had twelve durable mismatches on 0.2.8 and zero on 0.2.9.

Several fixes we had already written were no-ops. We removed them.

The bug was two dependencies down and the broken word was still in LodeFlow. I could wait for the stack underneath me to solve it, work around it and hope the workaround held, or keep going until I understood it. In this case I kept going. Not because every application needs to own a glyph pipeline, but because the terminal and the Markdown are most of what you look at in this one and I care if the text feels wrong.

I think this is part of why I like Ghostty.

Fast terminals already existed. Feature-rich terminals existed. Native terminals definitely existed. Mitchell Hashimoto’s complaint was that they kept making him choose two, so Ghostty chose all three: fast, feature-rich, and native.

Owning a GPU renderer that feels native on more than one platform also means fonts are not just picking a nice monospace face. They are discovery, fallback, shaping, grid metrics, ligatures, emoji, CJK, colour, rasterization, scaling, and getting all of those to agree about what one cell means. Get any one of them slightly wrong and the thing I stare at all day feels slightly cheap.

Mitchell jokes that Ghostty is seventy percent a font-rendering engine and thirty percent a terminal emulator.

So why isn’t VS Code seventy percent a font renderer? Because VS Code chose different ground. Chromium and the platform text stack carry most of that fight while the editor puts its depth into the workbench, language tooling, and an extension ecosystem. Ghostty owns its renderer and promises native text, so the font stack became its problem.

I did not switch because I knew any of this. I switched because Ghostty felt fast and complete and like it belonged on the operating system. I only learned about the font work later, when I went looking for why.

Meanwhile I was staring at a Markdown word that was the wrong size because of a cache two dependencies down. Ghostty cared about text far enough down the stack that I never had to know about the work before I switched. I did not need to build a renderer, but when text went wrong two dependencies down I had to decide whether that made it somebody else’s bug. It didn’t.

That is how the terminal widget and glyph atlas became my two forks. I could have left the Markdown word the wrong size and spent that week somewhere else. I didn’t.

The hills I am choosing

I had already considered writing a terminal emulator in assembly, so apparently my instincts could not be left unsupervised.

The challenge was to keep cutting until LodeFlow got more atomic. A complete code editor, a general terminal multiplexer, more platforms, more agents, a broader file browser: all of those sounded useful and, with an AI sitting there willing to take a swing at them, increasingly buildable. I needed a better razor than “could somebody use this?” because every idea passed that test.

Ghostty could probably process payments. It would not become a better terminal.

Keeping an agent process alive after LodeFlow closed barely registered as a decision at this point. I was focused on a crisp local editor I could open, see the whole fleet in, do the work, and come back to later. It was not a grand refusal. I just wasn’t investing there.

What I keep coming back to is the session. I want to know which agent needs me, which account it is using, what it changed while I was gone, whether the change worked, how much context it ate getting there, and whether three other sessions already learned the thing this one is about to rediscover.

Some of that exists now. The fleet shows working, waiting, and finished state. Sessions carry their account identity. A completed turn can hand back a change summary, a diff, and the commands and outcomes that support it.

The introspection part is not built yet. The agent CLIs already leave transcripts, token usage, tool calls, lifecycle events, and file changes all over the disk, but none of it can yet tell me how I am working. I want to come back after a week and find out which sessions kept rereading the same files, which projects spent the most context and what came out of those turns, or which agent reached the edge of its window while I was looking somewhere else. Later, I want a project to remember what earlier sessions established and show me where that memory came from before I trust it.

I want to spend the time there.

Then Samy sent me two lines

A while later I sent my buddy Samy a screenshot of LodeFlow and told him what I was working on for the first time. His response, verbatim:

Have you heard of herdr

Hehe

I had not. Merde.

Had I failed at the buy step?

herdr is a terminal multiplexer and runtime for coding agents. It hosts the real CLIs in real terminals, knows when an agent is working or blocked, and keeps the process alive in a background session so you can detach and come back later. It detects around twenty agent CLIs, it is Apache-2.0 on GitHub with north of thirty thousand stars, and it went through Y Combinator. I am a closed-source binary with a LinkedIn post.

Which was a little awkward, because near the beginning of this whole thing I had asked whether I could buy the answer and decided I could not. Now here was another Rust binary built around real terminals, agent-aware state, and a fleet of sessions.

Had I missed it? Yes.

I had treated “can I buy it?” like a box to check before I got to the fun part. In the AI world, where a useful little project can appear between two searches, that is probably backwards. The cheaper building gets, the more seriously I should ask whether somebody already built the bit I need.

If I had found herdr during that first search, would I have just used it? Had I spent months building something herdr already covered?

The moat is getting narrower

The old enterprise software moat is partly just the cost of rebuilding the pile. Part of what lets Salesforce and Jira hold their price is that replacing the years of features, integrations, migrations, and weird company-specific behaviour around them is a terrifying project. Even if everyone complains, the other side of the moat is very far away.

AI does not need to make all of Salesforce in a weekend for that moat to get smaller. It needs to make the slice one company actually uses cheaply enough that crossing starts to look less terrifying. This is the SaaSpocalypse version of the problem: if I can recreate the useful part of your product faster than I can get through your sales process, the size of the pile is not protecting it the way it used to.

Steve Yegge makes a related point in Software Survival 3.0. He puts Git and grep in the same bucket — too high a bar to aim at, so far past worth-reinventing that nobody is really trying. They earn it differently. Git has decades of decisions and terrible edge cases packed into it, and rebuilding it means volunteering to learn all of those edge cases again to arrive back at Git. Grep is small, atomic, absurdly cheap to run, and useful everywhere. Rebuilding either one buys you very little.

Most application software is messier than that. If an adequate project tracker, terminal, or agent host gets cheap to make, then a broad surface of adequate features stops being much of a defence. My bet is that the investment matters more than the sprawl. Making the broad, fine version is getting cheaper. The harder part is knowing which little corner you care about enough to keep digging into after it already works.

A friend’s project, DeskPlot, is a good example. He picked the infinite canvas: code, Git, terminals, and agents as panes on one desktop. Making that feel right sent him all the way down to GPU-rendered text that stays crisp while the whole thing pans and zooms.

Insects, birds, bats, and pterosaurs all wound up with wings because the air gives flying things a fairly strict set of problems. Agent tools have their own air: the models behave the same way, the terminal protocols are public, and a bunch of us are annoyed by the same things. Real CLIs, working and blocked session state, and native terminal hosts keep turning up.

Insects kept their legs. Bats spent their hands.

Same wings, different animal

herdr is built around keeping the process alive. A background server owns the terminal, sessions survive detach, and clients can come back over SSH.

In LodeFlow I am keeping the record around a session: the account, the changes, the proof, the context, and eventually the patterns across the fleet and the things a project should remember. For that I need a window I return to and a lot of information about what happened after I looked away. I am less interested in whether the process survived the night.

So yes, I had failed the buy step if the thing I wanted was a runtime for coding agents. herdr had built one, and its choices make a lot of sense. But that was not the question the digital fish had left me with. I did not need the process to live forever. I needed to know what it had done while I was not looking.

Finding herdr did change LodeFlow. I stopped treating faithful terminal hosting as the interesting part by itself. I went back through the list and cut a wait --until blocked event subscription whose actual job would have been letting outside programs coordinate agents. A route whose whole job is to sit and wait would have parked the single-threaded accept loop that carries every lifecycle event, so the elegant API came at the price of the data plane underneath it. Useful, but it was also me wandering into runtime territory.

I did not add a daemon. I wrote a sentence at the top of a design note:

herdr’s durable object is a process. Ours is a record of work.

Can, if you happen to read this, hi. I would genuinely like to compare notes sometime. I am curious what a runtime that keeps the process alive and a workspace that keeps the record should be able to hand each other.

And if your own setup is hung on spit and wires, LodeFlow is public today. I hope it fixes your particular mess too.