A long session inside Framnir
Framnir is a small, from-scratch x86 operating system. Its own kernel, its own window manager, its own set of userspace apps, all written in C. Most of what happens in a session like this one is just following a thread through a few thousand lines of existing code, understanding why it was built that way, and deciding whether to leave it alone or pull. This one pulled on quite a few threads.
One HTTP client instead of five
Several different tools in Framnir each had their own way of making HTTP requests. A REST API testing app, an AI chat client, and the command-line wget tool had each independently written code to open a socket, negotiate TLS, and parse a response. Three slightly different implementations of the same idea, each with its own slightly different bugs.
I built one shared HTTP library that both the kernel and regular apps could use, then migrated everything over to it one piece at a time. The REST client and the AI chat app went first. They were still living as special kernel-only applets rather than ordinary programs, and converting them to normal userspace apps was reason enough to switch them onto the shared library while I was in there.
wget came later and turned out to be the more interesting case. Its old code had specific logic to handle a wrinkle of the HTTP protocol called chunked transfer encoding, where a server sends a response in pieces rather than all at once. The new shared library did not handle that at all. Nobody had needed it yet. Rather than give wget its own private workaround again, which would have defeated the point of unifying everything, the fix went into the shared library itself. Every future consumer gets it for free.

Mail gets rethought
The mail client was a bigger question than a simple conversion. Unlike the REST and chat tools, a mailbox is not a stateless thing you can safely poke at from multiple places. POP3 downloads and deletes messages, and having two things do that at once risks losing mail. I decided to leave the actual mail protocol handling inside the kernel as a small background service, and make the visible mail app a dumb client that just displays whatever the service has already downloaded and asks it to sync or send. Whether the IRC chat client could get the same userspace treatment got a full look too, and turned out to be more approachable, though it surfaced its own gaps. A missing way to tell a timed-out connection apart from a dead one, among others. I left it as a plan rather than acting on it.
Once the mail redesign was in and being used for real, a run of small, funny bugs turned up one after another. The message list was quietly showing duplicate emails because of a leftover two-pass scanning trick that no longer made sense. The background sync service was starting up at boot even when a setting explicitly said not to. And once that was fixed, clicking sync for the first time would silently do nothing for several seconds, because a delay meant only for unattended background checks was also being applied to a sync the user had just explicitly asked for. Each of these was code that made sense in its original context landing awkwardly in a new one.
Tidying the taskbar
The Start menu's configuration used to live in the main system config file as a numbered list. Add an item in the middle, and everything after it needed renumbering by hand. I replaced it with a plain text file, one menu entry per line, where order in the file is the order in the menu. Icons in that file can now also be referred to by name instead of a bare number, which meant teaching the system to look icon names up against the same list it already used to build its icon catalog, rather than maintaining a second copy of the same information.
While looking at a separate memory setting, a stranger thing turned up. A configuration option that looked entirely legitimate, commented, documented, with a specific number attached, but nothing in the kernel actually read it. It had drifted into being pure fiction at some point, describing a limit that no longer existed anywhere in the code, so I deleted it.
Two crashes
A voxel-style building game called Blockada got a stability pass. The most serious finding was a large memory allocation for the game world that was never checked for failure. If it failed, the game would not crash immediately. It would wait until the player actually started building a world and then fail in a much more confusing way. An assumption about how much memory a single app was allowed to use turned out to be wrong too, corrected by testing it directly rather than trusting an old comment.
The more interesting crash was in Tootsie, a small social media client, and it came in two rounds. The first fix addressed a real bug. A background window that could end up with a stale reference if two of them were opened close together. But the app kept crashing anyway, on every single attempt, not just an occasional one. That inconsistency was the tell that the first fix, while correct on its own terms, had not found the actual problem. Getting to the bottom of it meant asking for a real crash report from the running system, matching it against the exact program binary sitting in the project, and reading the raw machine instructions at the crash site. They pointed to a basic, wrong assumption about exactly when a newly created window becomes ready to use. The original code, and the first attempted fix, had both assumed a certain setup step happened immediately. It does not. It happens slightly later, and that gap was enough to leave a pointer unset every single time.
Several of these threads are the same lesson wearing different clothes. Code that looks self-evidently correct in a comment, that "this happens synchronously" or "this setting caps memory at 16 MB" or "only one of these is ever created at a time," is worth checking against the actual source before building on top of it. And the two mail-service bugs are really one idea told twice: when a feature is meant to be off, turning it on anyway just to be safe is not a fix. It is a different bug wearing a fix's clothes. The honest version is almost always a bit more work. Teaching the thing to start itself on demand, or checking a real disassembly instead of guessing. But it is the version that actually holds up.
Reading this back, it looks like a session that could have been wrapped up in an afternoon. A single shared library, one mail redesign, a config format swap, two bug fixes. That is how it reads on the page, but that is not how it felt. Each piece meant reading through code I had not touched in months, sometimes years, trying to remember why I had done something a certain way and whether that reason still held. The HTTP library had to work correctly the first time because every tool in the system would soon depend on it, so I tested each path against real servers and fixed the things that broke. The mail redesign meant tracing through the POP3 and SMTP code carefully enough to split it cleanly without losing messages or leaving a lock held somewhere. The taskbar change looked like a five-minute edit until I realized the icon name lookup had no way to resolve names against the catalog, which meant untangling how the catalog was built and deciding where the resolution step belonged. The Tootsie crash took the longest because I was certain I had found the problem on the first pass. I had not. I had found a problem, just not the one that mattered, and convincing myself of that difference took longer than the actual fix.
A session like this one is mostly reading and thinking and occasionally writing a line that takes three seconds to type but an hour to arrive at. The ten-minute version exists only in the telling, and even then only if you skip the parts where nothing happened.
↯ Siamo equilibristi, siamo ballerini, siamo giganti, siamo dei bambini, in un film di Federico Fellini.