Notes for August 24–28

I came back to work this week, started catching up on everything, and decided to go out for groceries without an umbrella (it is still August, right?) and got drenched.

That might be why I ended up having to take a day off–the fact that there was a virus rampaging through the household for a few days might have been entirely coincidental. But it removed all the momentum from my return to work and didn’t improve my ongoing shopping list of ailments (back, plantar fasciitis, etc.).

Trying to get some exercise in by going to the pharmacy was not on my bingo card.

Musings On Poverty

Since misery loves company, I followed up on by pulling up a spreadsheet and trying to figure out how deep I would be in the hole if I bought a new desktop Mac, and… nope. I don’t think I am going to buy any new Apple hardware this year (unless something critical breaks). In fact, maybe no hardware whatsoever except for reviews, which just feels weird.

I got some very generous recently, though, so expect interesting hardware write-ups soon.

Squeezing H.264 into Unlikely Places

In that spirit, I revisited a little hack that let me keep using old hardware: we’ve had M5Stack ESP32 cameras in the house , but Apple, in their wisdom, broke repeatedly until they stopped working (to be fair, they probably shouldn’t have worked in the first place).

I’ve used them to keep tabs on 3D printers, chimneys (it’s extremely irritating when your upstairs neighbours decide to renovate their kitchen), and other things, but they had become kind of useless since didn’t support their MJPEG streams and I really don’t want to use anything but the Home app.

Well, it turns out that paid off: I realised that, yes, an ESP32 can do H.264 encoding, and spent a while iterating on the problem with gpt-5.6-sol–first getting encoding to work, then trying to figure out why didn’t like the stream and mapping out all the parameters involved, and finally adding caching and interleaving encoding with streaming until I got 5fps (which is enough for most uses).

This led to a somewhat off-the-cuff HN comment, which I reproduce below:

As I was reading this, I realized that the ESP32 HomeKit debugging session I had running was taking a while, and this happened:

Me: What are you doing?

Agent: I’m resolving Apple’s private video-resolution lookup table to determine whether iOS 26 supports 320×240 for HomeKit live streaming. The decompiled source hides the numeric constants, so I:

  • Downloaded/extracted the iOS 26.1 arm64e dyld cache to /workspace/tmp/ios26-dyld/.
  • Built and installed the read-only apfs-fuse utility needed to mount Apple’s APFS image.
  • Planned to extract HomeKitDaemon and decode its resolution table.

Me: Oh, OK. Carry on then.

These are weird times.

I was using piclaw for this, but a few days later Codex pulled a very similar stunt on me–I am increasingly of the opinion that sol is perhaps a bit too proactive…

Unsocial

This week I cancelled my LinkedIn Premium subscription, which is ironic in to comment here, but doing so felt long overdue. The reason I did it is that even with a corporate discount I was getting negative value from it:

  • It has devolved into a vanity fair that is worse than Twitter (if that is possible, solely because I still get some value from my daily 15 minutes of Twitter).
  • There was absolutely zero sense in paying for any of the “perks” it offers, all of which I have used approximately zero times.
  • The spam was getting really annoying, even after I patiently disabled everything.
  • People I meet at work can use my e-mail address to connect with me, so contact discovery is moot.
  • And, in a shocking, shocking development over the past few years, recruiters don’t really use it for recruiting any more–they just spam you without even checking if you’re a match.

None of that is worth the money–unless they were paying me to use it.

I can’t really remove it from my phone yet, but… we’re getting there.

Cleaning Up

I also spent a fair amount of my downtime filing e-mail and poking at a few interesting things, largely to keep myself organised.

For good measure, I decided to take another stab at the “personal CRM” thing–doing reviews is great, but you end up asking yourself “what is the state of X?” too often, so I got piclaw to whip up a moderately sensible data schema:

Entity relationship diagram for contacts, organisations, messages and work pipelines
The data model behind my personal CRM experiment.

The key thing here is that there is no UI and no integrations–I just drop e-mails (or entire mailboxes) into a chat, and piclaw files them away and reasons about them. Retrieving data is, as you’d expect, trivial:

  • If I want to see the overall status, I get an interactive Kanban board in the piclaw UI.
  • If I ask about someone or something, I get search results and a little table of our correspondence, including a plain-English summary of what transpired.

I thought about adding this to Memento, but the transactional and threading parts wouldn’t be a good fit (the CRM handling skills and modus operandi went there automatically, though, which was nice).

Addendum: Apparently It Can Ship Things Now

Turns out sol’s proactivity has manifested again. I asked it to review and test the secure P2P messaging add-on, and, without any intervention on my part, it provisioned two additional piclaw instances, tested the add-on across all three and then cut the 2.15.0 release.

Sol provisioning two additional Piclaw instances, testing Remote Peer across all three and publishing Piclaw 2.15.0
I only asked it to review and test the add-on.

To paraphrase Douglas Adams on children and off switches, I may need to work out where sol’s is…

Notes for August 17–23

Tomorrow I’m going back to work after a rather lopsided summer break (not feeling up to it physically or mentally), and the social network pendulum is swinging back into a bit of normalcy as I progressively tune nearly all of them out.

Twitter (which I refuse to name otherwise) is still a controversial (but time-boxed) part of my news intake since has turned into a desert for any serious discussions other than violent opposition, LinkedIn is still crammed with sales-y ego trips and keeps peddling glitz rather than substance, but I do need to figure out what is going on.

For science, I dipped into a few of them over the weekend, which did not improve my general mood–it doesn’t look like I am going “back” into a saner industry (rather the opposite), but at least I sort of got my book reading habits back over the break, and I can finally say I did more productive stuff than just doomscrolling.

3D Stuff

First my shaver broke in the most subtly annoying way (the retaining latch was clearly not designed to outlive the mechanism), then I decided to do something about designing and printing cases for a bunch of SBCs and realised I no longer need to buy dust mesh for those:

A few things that needed fixing
A few things that needed fixing

Turns out I can print 0.1 mm-thick dust filters with zero issues using PLA and a 0.4 mm nozzle, even if the prints themselves are a bit susceptible to minor flaws if the filament is aged. But being able to design them to size (and shape) is promising.

The Future Is Not Evenly Distributed

I ended up trying on the only machine I have that can somewhat run it at speed (my MacBook Pro M3, which has 36GB RAM), and… it sort of worked (with RAM to spare, which is nice), but quickly reminded me why I prefer doing on servers as the battery visibly drained and the machine became warm enough on my lap to be noticeable.

Doing small programming tasks (algorithms or singleton functions) without an agentic harness context was doable (so I could probably rely on it if I was doing the same stuff I needed three years ago), but it takes forever (something like 30 minutes) to churn through a basic sub-32Ktok agent context, so… I still need better hardware.

Claw in a Bun

Under the pretext of upgrading to , I did some of the long-standing cleanup work in piclaw, dropped in a few fixes and dependency updates, and let it loose.

As recounted , I just want to use the thing now, so unless there’s some amazing new frontier model out or some critical fix, I’m going to try to throttle back on it–also, my free Codex subscription is running out soon, so when that goes I’ll have to re-think how to keep my work and hobbies separate.

But things are looking up–RAM use seems lower, most of the annoyances I had seem to have been fixed, and there are (crosses fingers) no immediate issues.

Homebridge 2.4

After a very careful waiting period, I finally updated to homebridge 2.x, which, of course, broke things. In my case I am using homebridge-webos-tv to control my living room TV and homebridge-mqtt to create virtual HomeKit devices from , and they both broke instantly when I upgraded to 2.4.0 (yeah, I waited that long) because they seem to be fairly niche and not fully tested with 2.x yet.

The solution (which took a while to figure out) was to disable their child bridges and force the use of the bonjour-hap advertiser in the top config stanza:

"bridge": {
  "name": "Homebridge",
  "username": "0E:…",
  "port": 51826,
  "pin": "123-45-678",
  "advertiser": "bonjour-hap" <- ADDED THIS
}

At this time, nearly all of my automations are pure native HomeKit, with only a few things patched through (which I upgraded to 5.0.4 this week as well), which is great except that it exposes me to Apple’s general penchant for breaking things every now and then.

But everything “just worked” aside from a minor configuration glitch in a “Virtual Bell” accessory that I use to merge our doorbells into a single alert, and my usual fiends, the ESP32 cameras that we still use to check on open windows and stuff.

Those tiny and slow cameras are long overdue for replacement, possibly with something like the Aqara G100. The M5Stack hardware was never really meant to be compatible, but I’ve been hacking at the firmware for years and they have suffered through years of Apple breaking things subtly (without even documenting the right behaviour for camera devices), so at this point I keep them around for a sort of sport.

Minor Annoyances

If you’ve missed my , well, I’ve been trying to get a lot of writing done on my iPad, and… stuff happened. I’ve also put together a swift-app-template to see if I can standardise a bit the stuff I am doing, and made a point of cramming in as many quality-control steps as possible so I don’t keep reinventing the wheel, starting with SwiftPM defaults and ending with a comprehensive set of agent skills consolidated from my library.

I also pinned down and fixed a site rendering bug (images inserted using certain ancient formats of references would be interpreted as page links).

Reading

Building on last week’s book marathon, I finished The Shattering Peace by John Scalzi, which was a nice, fun romp, and got started (again) on Norse Mythology, still one of my favourite reads.

I think I’m ready for whatever Loki tosses at me tomorrow when I log back in.

Apple Papercuts Summer Edition

This is a short follow-up to my piece, wherein I bunched together a few more annoyances that I’ve come across while using my extensively on vacation.

If you needed any more proof that doesn’t really do QA testing, I think just two of these would be enough. And I’ve confirmed they are all still there in 26.6.1, freshly released last week.

The Spotlight Near Miss

At first, I thought this was a hardware problem. My brand new had been giving me some odd glitches of late (I keep having to physically reconnect it for keypresses to register), but after a few days of using a desktop Bluetooth keyboard I am positive that all the input glitches I’m having are a software problem.

One of the main symptoms is that invoking Spotlight with Cmd+Space works but then typing inside Spotlight fails to register. But a second attempt works, so my attempts at invoking Mail every day have the same outcome:

  • First attempt at Cmd+Space, M… does nothing useful–Spotlight appears but doesn’t show “m”
  • Hit Cmd+Space, M again–“m” appears and, lo and behold, “Mail (open)” is suggested (after an embarrassingly long pause, but hey, at least it happens)

Yes, I could tap the icon. But if the above doesn’t convince you that nobody at Apple actually tested this, then I don’t know what would. Oh, wait, I have a couple more:

The North Star Is Portrait Orientation

Another interesting bug that has surfaced recently is that nearly every time I use a Bluetooth mouse with my iPad when it’s plugged into an external display, iPadOS swaps the X and Y axes, which is just… ridiculous.

The steps are always the same:

  • Wake up the iPad while it’s plugged into an external display (typically by swiping the Home Screen up so that Face ID begrudgingly notices I’m here).
  • Turn the mouse on, move it up
  • Wonder why the cursor is moving left

You know why that happens? Because my iPad is in landscape mode, not portrait, and when the mouse is detected, iPadOS does not take that into account. But the Home Screen was in landscape mode. I swiped “up”. This is not fucking rocket science, guys.

And, again, for this one there are zero excuses.

SpringBoard Needs Frequent Breaks

Every now and then, while my iPad is plugged into an external display, my entire iPadOS “session” crashes–black screen, emergency spinner on the iPad display, everything gone (well, fake gone, just like the fake iPadOS multitasking).

Over my break, I mostly ran , , and , with some cameos by various terminal emulators and the usual social media garbage. None of them are particularly taxing, and the vast majority of my external display time was spent writing or looking up references.

This is not a stability story. It’s not even a reliability story. Having your session wipe out like this is simply unacceptable.

You Want to Do What With Your Windows?

This one’s a twofer: We all know iPad window management is sub-par––but there are two things that annoy me to no end:

  • Swiping up on your iPad display to flip through applications completely kills keyboard input on the external screen until you Cmd+Tab back to the application you were typing in
  • You cannot even think about picking which windows get assigned to quarter screens.

This last one is tantamount to the Odyssey, and it should probably be on IMAX due solely to its dramatic plot.

After painstakingly inching your mouse towards the almost invisible semaphores, clicking on them to make them actually usable even though they pulse and expand when moused over just to tease you, you then have to play the green button roulette.

That may, according to its internal whims and in a complete travesty of UX polish (that we, the elders, remember from ancient times), either maximise the current window or (maybe) show you a menu.

If you are worthy of the gods (or just plain lucky), you will finally get a window layout menu and get to pick “Fill and Arrange”, which has completely random behaviour, making it impossible to figure out where your windows will end up, and leading you to believe you have any choice in the matter when you later try to arrange them manually.

And no, you can’t have a right-first half-screen arrangement, either. You are not that worthy.

Building Piclaw on Top of an Opinionated Coding Agent

I’ve spent the better part of six months building piclaw–my personal AI assistant, workspace and, occasionally, agent swarm–on top of Mario Zechner’s engine, and I think it’s time to write about not just my motivation but also how I feel about having invested that much time into the whole thing.

This is important (at least to me) because is an excellent coding agent with a very clear point of view about how things should work. The internals are concise, well structured and easy to follow, with the brilliant twist that extensions can literally take over the entire thing–to the point where you can pretty much rewrite the entire agent loop (however you care to define loop these days).

Building a different kind of UI on top of it has been educational, very rewarding and occasionally maddening, and I’ve reached the point where I need to write down what I’ve learned and a lot of the underlying rationale–partly because I just want to use the thing now and move on, and partly because there’s a lot buried in the project documentation itself (there are a lot of whats and hows, but relatively few whys).

Why pi?

The key thing that captivated me is that is, in effect, a self-modifying agent harness.

Yes, self-modifying. Like… a virus. I like to think that its virality (however you cast it) is one of the unstated reasons it has found its way into a lot of projects lately, but let’s skip that bit and just reframe “self-modifying” as both self-improving and self-evolving without dipping too much into the penny arcade theatrics of AGI.

I’ve come to the point where I think that even without agency, self-modifying harnesses like pi are way more powerful than just about anything we’re getting from megacorps, because they can morph into your own completely personalised environment–which is what piclaw became for me.

When I started out fooling around with what eventually became steward, the field of open coding agents was a mess of half-finished scripts and forks, and stood out because it was opinionated in the right ways: as the runtime, a clean turn-based conversation model, good terminal UX and–critically–a willingness to evolve quickly. Mario Zechner made strong choices about tool calling, context management and model interaction, and they were essentially the right ones.

And if you take the essential bit literally, the thing got most right was restraint. Or, if you have been following the philosophical debate about AI use, taste.

At the risk of reiterating the basics most people already know about , it starts with a small, carefully chosen set of built-in tools–file I/O, shell, search and editing–and leaves the LLM to fend for itself in UNIX land, which gives it tremendous power and flexibility.

Compare that with what AI lab harnesses are becoming: sprawling collections of specialised tools, bespoke UI panels, inline diffs, notebook integrations, code review modes and whatever else someone thought of that quarter week. Every one of those features eats context, adds latency and makes prompts longer–which means the model has less room to think about your actual problem.

starts from the other end: give the model the minimum surface it needs to be effective and spend the rest of the token budget on the actual problems at hand. The introspection is excellent too–you can see what the model is doing, which tools it is calling and what the context looks like at any point. No magic, no hidden system prompts, no mystery. That transparency is worth more to me than feature polish, because it means I can debug things when they go wrong.

I didn’t necessarily want just a coding agent, though, and after a while, I realised I didn’t even want to run it in a terminal. I wanted an extensible agent that could run headless, manage my homelab, edit my wiki, file links, take notes, run scheduled tasks, and generally be useful in ways that might only occasionally have to do with writing code.

’s small tool surface and clean turn model were close enough to what I needed that building on top of it seemed faster than starting from scratch.

Escaping The Terminal

As I’ve written , I don’t run coding agents locally–that’s why I spent quite some time coming up with things like agentbox and webterm to run them in remote machines.

But the biggest departure from came from using it inside vibes, which started out as a generic web UI that I could use from my phone. And with my mobile background, it was pretty obvious (at least to me) that a highly optimised web UI was vastly better than using WhatsApp or Telegram as a bridge–not only could I make it much more secure over (without any middlemen), but I could build a much nicer, continuous experience across devices, reconnecting from anywhere and picking up exactly where you left off:

The same Piclaw session open across several devices
The same Piclaw session across devices

Another key factor is that I almost invariably begin and end my days on an iPad, which pretty much defined the rest of the UX–I can reference timeline messages, annotate them with Apple Pencil, etc., making for somewhat terse but very effective interactions, especially when doing reviews. I just toggle between Safari tabs to move between chat and GitHub or Gitea diffs, and with a little bit of AGENTS.md tweaking or per-session prompting things can be very productive.

Piclaw open in Safari on an iPad
Piclaw on the iPad

The entire thing unfolds like origami–files on one side, plan trackers on the other, documents opening into tabs or detached panes, and ample touch support throughout (resizing panes, swiping between chats, etc.). I also took advantage of pi’s brilliant session tree internals to create multiple “agents” (that can talk to each other inside each instance using a chat tool, and even across instances…)

The Piclaw interface with its timeline, plan and utility panes open
This is what Piclaw looks like to me these days
You’ll notice that the above is a bit… crowded. And I am not even using the file explorer or the built-in terminal, but it is pretty much how I use `piclaw` on a daily basis. From the top, you can see:
  • Several agents that I can switch quickly between (in this case they’re actually coordinating to fix an add-on and deploy it across the “fleet”)
  • A timeline with rich link previews, my latest prompt, and three temporary panes:
    • Thinking output from the model
    • Tool output
    • Tool execution details (what, current working directory, and git branch)
  • A plan sidebar that the model started using without prompting (this is gpt-5.6-sol, so it tends to remember previous instructions)
  • A compose box with model status/plan/context information (you can’t see the submission, previous message references and image attachment widgetry in this shot)

The top dashboard and the side pane are add-ons, and they’re designed to be used on demand, so the typical UX is much less cluttered–but my key point is that despite several attempts we’ve made at “improving” it, this has been working out really well for me on the iPad with both voice and Apple Pencil input, and I seldom use anything else these days.

One thing you can’t see is that it is running on a remote VM–piclaw has a handy little widget I can pop up on the top right-hand side that gives me sandbox instance info:

Piclaw widget showing resource usage for a remote instance
Remote instance stats
A colleague and I dabbled with a Visual Studio-like UX, but I still prefer the fully responsive front-end, since it evolved to match my peculiar working style:
  • Constantly switching between devices (iPad, work laptop, iPhone, work AVD, MacBook, Linux laptop, etc.)
  • Multiple concurrent sessions (typically one per project) in each instance.
  • Very long sessions that can last weeks.

Unlike people who have the luxury of uninterrupted focus time and can work on only one thing at once, I have to switch constantly between meetings, coding, planning, researching, writing, etc. I tend to work on some things in very short bursts over quite long periods of time–and having something stuck in a terminal someplace, even over tmux and ssh, would never cut it.

This is probably the biggest reason why I’ve never considered piclaw a “product”, even though it naturally evolved many of the trappings of other coding agents–including external add-ons.

Extending pi Into The Browser

But the key thing is that all the above are just extensions, since I leaned hard on ’s extension mechanism, extending it to support the web interface.

pi has a very capable terminal UI, and its extension contract exposes a bunch of it, but a lot of its flexibility doesn’t (or didn’t) cross the IPC interface, so I gave piclaw its own browser UI contract: extensions are route- and event-aware, and the shell extension host can mount, focus, resize, save, detach or dispose of them.

They retain the standard pi extension features, but can provide editors, file viewers, terminals and even a VNC client without reaching into the main application bundle. And, of course, they can do all sorts of fancy widgetry in the timeline (which has used from the start and can serialise widget states).

There are many things that are just intrinsic to piclaw, of course. The piclaw web server provides a bunch of scaffolding for managing UI panes, SSE events, settings, the works, plus there is an encrypted keychain, database and state introspection for the chat/sessions and file attachments in the timeline, etc.

But most of the shipping extensions started out like this:

{
  "piclaw": {
    "compatibleVersions": ">=1.8.0",
    "tags": ["example"]
  },
  "pi": {
    "extensions": ["index.ts"],
    "web": {
      "entries": ["web/index.ts"]
    }
  }
}

This translates into actual code like so:

// index.ts -- a normal Pi extension, loaded by the agent runtime
// schema and handlers omitted
export default function example(pi: ExtensionAPI) {
  pi.registerTool({ name: "example", parameters, execute });
  pi.on("session_shutdown", closeResources);
}

// web/index.ts -- loaded separately by Piclaw in the browser
__piclaw_web.registerPane({
  id: "example-viewer",
  label: "Example",
  capabilities: ["preview"],
  placement: "tabs",
  canHandle: ({ path }) => path?.endsWith(".example") ? 100 : false,
  mount(container, context) {
    return createExamplePane(container, context);
  }
});

Slowly, gradually, piclaw effectively built its own UX out of what are essentially all pi extensions, which I think is a great example of how good the original design was.

Earendil’s Moving Castle

Now, the design still is good, but the problem with building on top of someone else’s opinionated engine is that, to an outside observer, their opinions keep changing. is very actively developed–which is good–but there was a period when every upstream bump brought a non-trivial risk of things breaking in ways that were hard to diagnose.

I am very happy with piclaw as a tool to the point where I seldom use anything else, but I want to truly use it rather than spend so much time maintaining it, and over six months I’ve dealt with:

  • Tool-calling interfaces changing shape
  • Context-window management behaving differently after upstream refactors, causing my longer system prompts to be silently truncated.
  • The model configuration layer being restructured, each time requiring changes to my settings and onboarding code.
  • Streaming behaviour changing subtly enough that my web UI’s SSE handling would occasionally drop tokens or duplicate thinking indicators.

A later refactor to model handling fortunately converged with my approach, but still required a day of coddling GPT-5.6–very soon after I had spent two days integrating and testing a new context-compaction approach.

In a way, all of the above was useful because piclaw now has a gazillion regression tests–not just for the core and event loop (since my event loop has to do a fair bit more than “just” running a TUI, like routing SSE events, updating tables, etc.) but also for the UX (including a very spiffy PDF report that spans dozens of Playwright tests).

So the first “new” thing I learned out of the whole thing was how to apply the TDD mantra I had to follow in a few customer projects to piclaw, including getting the LLMs to generate decent user stories (and Gherkin) for tests and making sure the entire thing can run in Actions. I’d done it before, but not at this scale and depth for a single project.

This makes builds slow, but it’s completely consistent with my of ensuring LLMs generate useful code, and it’s been pretty rewarding.

The flip side is that spending three and a half evenings on compatibility work when upstream broke stuff I didn’t expect sucked. However, I don’t blame Mario for this. It had to happen.

The core of pi has got to be a very hard thing to reshape and evolve (especially considering the explosive popularity it’s been enjoying), and the constant model and provider churn certainly doesn’t help. If you peek under the hood, the sheer amount of work poured into pi-ai just to talk to provider endpoints is batshit insane, and is possibly tracking more moving targets than NORAD.

Sixteen Releases Later

Since I first dated this draft on June 19th, piclaw has gone through sixteen distinct Earendil releases, from 0.79.8 to 0.84.1. Most were uneventful dependency bumps, but package/API splits and a ModelRuntime rewrite for provider discovery, credentials and model catalogues prompted another couple of late evenings, since I now rely upon the latter for my own carefully crafted and GitHub extensions (because, well, I need to dynamically resolve models ).

I could just let upstream stabilise, sure, but the way things are going I’m pretty sure that will take years, and none of the above requires to promise API stability. But since just using the SDK and IPC isn’t really an option for me (at least not yet–more on that in a bit), I have to accept these as the natural consequences of shaping my working environment on top of a moving foundation–Earendil has more on their plate than catering to my somewhat eccentric use case.

Compaction pushed this furthest, since my style of work lends itself to very long sessions and I have access to multi-million token models, so my usage pattern seems to be very different than most.

Upstream added reasons, retry metadata, post-compaction estimates and eventually retry lifecycle events; my smart-compaction add-on grew summary validation, continuity checks, provider-specific execution and request-auth preservation around those hooks. When I implemented provider-native remote compaction, I then had to handle summaries that one provider could create but could not replay through another, etc. That made me respect the work done in pi-ai even more, by the way…

Fortunately, more recent upstream work has converged with decisions I had already made. Cache-friendly dynamic tool loading, full provider extensions, and compaction metadata plus retries for failed summaries are all things a long-running assistant needs, and let me ditch some of my workarounds. I still had to adapt piclaw’s credential store, model refresh, scheduler and extension loader, but I was no longer maintaining those ideas entirely outside the engine, and some of that scaffolding became integration code.

And I’ve finally caught up to the point where I am now proactively tracking the new upstream agent harness and started breaking down piclaw into individually testable “effectors” (for lack of a better word) that can just plug in to the new agent loop as it changes state.

With luck, I should be able to adopt the new harness as it comes out. Worst case scenario, I’ll have to refactor a bit more, but some of it is long overdue, to be honest.

But let’s take a little detour.

TypeScript, Bun and the RAM Problem

The other thing I’ve learned–and this is less about specifically than the ecosystem in general–is that the runtime overhead is brutal on constrained devices. is, to me, a massive improvement over Node.js (and its is one of the reasons I decided to finally clean up my notes and post this), but it still has its challenges.

Using 1.3, piclaw running on my ARM devices used somewhere between 400 and 600MB for the main process, depending on how many tools are loaded and how much conversation state is in memory. On a desktop or a VM that’s nothing, but on a smaller ARM board that is also trying to run builds, it is a meaningful chunk of RAM.

Early results with 1.4 (very early, since I just did the first piclaw release based on it) are… mixed. I have seen some of my instances go down to 200MB RSS, but others remain in the same range.

is fast, and I genuinely like working in for the web UI and tool surface (mostly linting and testing, honestly, because I still don’t really like the language). But the combination of JavaScript runtime overhead, the node_modules graph and a process that loads dozens of tool definitions at startup means I am paying a tax that a compiled language would avoid, and I am constantly reminded of that.

I still prefer (and continue to explore doing harnesses in it), so there’s a draw away from the internals of that I just cannot ignore.

Anyway, I adopted a few different tactics to try to keep things under control.

The Extension Split

The first mitigation was architectural, suitably -aligned, and typically lazy: I split piclaw into a core runtime and a set of lazy-loaded (ha!) extensions, hacking session management (which I also extended to support multiple parallel running sessions) to support dynamic loading.

So extensions like tooling, integration, , wiki helpers and other (incidentally) context-heavy tools are loaded only when the model needs them, rather than inflating the base process (or the session) from the start.

This helped, and eventually converged with ’s recent releases, which made this less of a hack as the core added its own dynamic tool loading (which was tricky for a bit since it mangled the cached prompt prefix in my custom handling…). I only use subprocesses for scheduled tasks and some add-ons, but I wired those through as well.

The thing is, shipping all those extensions with piclaw itself started becoming unmanageable, so…

Everything Gets An App Store

I eventually decided to move the larger integrations into a separate repository, making it possible to download and install things directly from it:

The Piclaw add-on library displayed in its management interface
The Piclaw add-on library

This created more maintenance, but it also forced me to treat extensions differently in general. The repository now has standalone-import tests, metadata validation, etc., and one of my slow burning tasks has been migrating stuff out of the core repository into what is now a nearly 50-strong add-on library.

That includes niche provider integrations, infrastructure clients, viewers and editors, delegation and compaction machinery, terminal renderers and small pieces of UI that make sense only in the web app. Each is installed as a Pi package and its runtime entry still receives ’s native ExtensionAPI, so tools, events, session state and shutdown semantics still belong to the core (which is why I’m quite optimistic about my chances of adopting its upcoming harness without a lot of breakage).

Also, some of the best improvements to my workflow came via contributed add-ons: delegate, in particular (which just invokes pi as one or more subprocesses, at least until I find the time to refactor it), has been pretty useful for things like “audit this PR independently using sol, terra and opus and give me an assessment”.

A Place For Every Setting

Oh, yes, and that scary thing around the add-ons library above is the Settings pane. Every extension in piclaw (bundled or add-on) has access to a settings contract that lets it create and render its own UX. Here’s the scheduled tasks pane, for instance.

Piclaw settings for scheduled tasks
The scheduled tasks settings pane

Settings panes use a fairly straightforward pattern: the browser talks to an authenticated /agent/addons/api/<addon>/<action> endpoint, the runtime registers the handler, non-secret values go into the extension KV store and passwords or tokens go into the keychain.

But there’s more to this than just the (slightly overkill) amount of functionality I built into piclaw. Using it has also forced me to reflect (and learn) a lot about how to effectively use LLMs across the board.

Knowledge, Memory And Regret

One of the things that became obvious after keeping sessions around for weeks is that the conversation itself is mostly disposable–but only just. The most useful bits are what survives outside it–files in the workspace, instructions in AGENTS.md, and skills.

Like everyone else, I leverage agent skills extensively, and have dozens of SKILL.md files and scripts that describe how I want recurring jobs done (including all the little caveats I would otherwise have to rediscover every few months).

That is a much saner kind of memory than stuffing old chats into a vector database and hoping similarity search will do the right thing, but I’ve found there are multiple complementary aspects of agent memory and ways to ensure reproducible behaviour.

To begin with, each new session gets the same workspace and operating conventions, plus enough current project state to resume work without dragging months of conversational debris into the context window.

But I’ve also incorporated a recurring self-diagnosis that does light tweaks to skills and a small set of “memory” files. Every night, an out-of-band “dreaming” session goes over recent activity, updates human-readable daily notes and distils the useful bits into preferences, corrections, project state and a compact startup index–while pruning things that no longer need to be folded into every session. It is deliberately lossy, and all the better for it.

Then there is late-night-regrets, my somewhat pointed name for an add-on that looks back at interaction history and classifies course corrections, misinterpretations, over-engineering, under-delivery, context failures and useful proactive work. Typically for me (if you’ve ever come across any of my news processing pipelines), the classifier is a small Bayesian affair that consumes no model tokens; a separate reflection pass rates high-confidence failures and turns them into notes or steering cues for later sessions in a feedback.md file that is referenced when starting a new session.

That is about as much “self-improvement” as I am willing to trust right now: review what happened, summarize what went wrong, and keep changes to a (very) limited set of files. There is no model secretly rewriting AGENTS.md at 2:30 in the morning–it just wakes up with better notes and, occasionally, a list of regrets.

But me being me, I had to take it a step further.

Memory Across Agents

Memento visual debugger showing shared memory nodes and diagnostics
Memento’s visual debugger

came out of wanting to carry some of that knowledge across machines and agents without sharing chats, credentials or piles of machine-local cruft. The durable concepts are Markdown in Git, split into namespaces and put through a proposal/curation flow, and I initially designed it to share skills and their associated scripts and assets. I hacked in my own embedding engine, but as usual with me it relies mostly on indexing rather than taking embeddings as the source of truth–which, given my experience with opaque memory systems, is an important distinction.

This is only a month or so old, but has already proven itself useful in very mundane ways, like allowing piclaw to replicate itself to new machines and bootstrap them with the shared skills available to every instance:

Piclaw setting itself up on a remote host
Setting up Piclaw on a remote host
Memento knows that I care about exact current-state reporting, clean Git history and verifying things on the actual target; it has engineering practices distilled from several unrelated projects; and it carries entire skills, including the bootstrap skill used above and the supervisor/worker “squad” model I use to keep sibling agent sessions from wandering off into the weeds.

The best thing? works very well with other agent harnesses, including on my iPad.

Where This Leaves Piclaw

piclaw is still my primary assistant, running on six machines and doing useful work every day. The extension architecture made it maintainable, and ’s upstream improvements continue to make it better.

But, again, it is a “product” for one. And it is quite likely to stay that way, but part of the reason for that is that I very strongly believe that this kind of thing is already a commodity and that it makes zero sense for anyone to invest on selling AI code editors, shells, etc. Partly because the people who can build these things are increasingly likely to do it for themselves, and partly because the least common denominator is already out there in a zillion form factors.

Regardless of everything I’ve learned in the process, the most interesting part (for me, at least) is that ’s philosophy is the key differentiator–having a self-modifying agent hardness to build from is what enabled me to very quickly build piclaw up from a web wrapper into my main working environment, with its own interface, extension model and accumulated habits, and I’m going to keep following that approach going forward.

Here’s to the next version–but first, I need to enjoy my last couple of vacation days.

The Age of Infinite Software

I am most definitely not the first person to mention this, but the past year has seen a Cambrian explosion of two things: AI deniers who base their judgement on very limited exposure (or effort to use it) and thousands of variations on software of all kinds, from the perennial to-do list to AI-infused toothpicks.

Which, let’s face it, is a bit much. It’s bad enough to go on social media and be pitched things I don’t need, but the hypefest around new startups hawking their wares to fill hitherto undiscovered “problems” in increasingly pointless sub-niches is a bit much too. I find the entrepreneurial energy heartening, but most of them solve no real problems whatsoever.

I mean, anyway? And workflow engines? And CRM pipelines? There is a dizzying amount of almost-identical AI-driven solutions out there trying to land the ideal five-word VC catchphrase, and it’s just tiresome at this point.

Plus if you take the long view, most of them are completely divorced from the realities of, say, traditional IT, which has turned into an ouroboros of complexity thanks to geological eras of non-technical decision-makers – but that’s beside the point right now.

The one thing that I find positive among all this (and that I’ve hinted at a few times in the past) is that can now create whatever tools they might need to scratch their particular itch(es) – the results may not be perfect, and they may have an audience of one, but they can unblock time, patience and capabilities they would otherwise lack and improve things in general for themselves, without having to conform to someone’s idea of a “product” or jumping through hoops to make their workflows fit a tool.

Of course, the flip side is that it is all too easy to go overboard (when you have AI, everything looks like a solvable problem), so restraint and taste – which is probably the most effective, even if somewhat passive-aggressive and snobbish criticism you can lob at people these days – are essential.

But this ability for people to design tailored solutions for themselves is, I think, what SaaS vendors (and system integrators) fear the most. In an age where anyone can point an LLM at a bunch of APIs and say “build me a CRM that works for me” (which is something I’ve seen done, to the horror of a friend of mine working at Salesforce), the “value” of customisations has to be replaced by something, and believe me, it’s not going to be a consulting firm’s business acumen – or even their taste (which is nearly always lacking).

That does not make the tools magical, of course – they still need and someone who understands the result.

But on a smaller, personal scale, it’s kind of ironic to think that probably the main reason this is going to kill the appeal of most SaaS is that we’ve spent a couple of decades killing native desktop development in favour of kludgy, half-baked web UIs that are all variations on lists of things people click on.

And believe me, if there is one thing that LLMs can do perfectly, it’s web interfaces with lists of things people click on.

Notes for August 9–16

This was a different week, partly because we decided to watch the eclipse from a Spanish beachfront and partly because I actually read three books. There is an entire sub-thread around my back and neck aching worse than ever and my sleep patterns looking like a game of Splatoon that I will spare my readership, though.

I do think that this week’s Economist completely nailed the holiday feeling since I nearly drafted an existential rant about considering retiring to Spain for tax purposes, but as the article says, a lot of holiday musings don’t really stand the test of time–something I’m not going to take for granted.

But hey, worked (hotel Wi-Fi seems to be mostly a solved problem by now, even if I still chuckle at WISPr portals), none of my home machines overheated while we were gone, and although the week was a dismal failure in terms of writing, I managed to, at least for a few hours, completely forget about work and actually enjoy myself.

But I really wish I didn’t have to go back. This has not been a new feeling over the past decade, and I have taken it to mean that I can finally reason about work in the proper way (either because it has stopped defining me or because it’s fundamentally misaligned with what I think I should be doing in these insane AI-driven times–not sure which yet).

Rust Isn’t That Shiny

One of the books I read was on (actually two, the original book’s third edition and another on Rust for embedded systems), and I plodded through its gospel mostly wondering what the heck I could really get out of it given that I’ve mostly settled on and for the vast majority of my projects this year that aren’t in or .

Although I’ve been successfully steering wrdp to passably structured form (and, more important, usefulness), I remain unconvinced I would have used to do it if it wasn’t for IronRDP, and given the malleability of and the ease with which most LLMs can generate it correctly (provided you and lint/fuzz the heck out of it), I don’t see myself picking as my default unless I’m going into a codebase that already uses it.

My biggest gripe remains the mental model, or, more to the point, the mental overhead of imposing a series of abstractions on top of the language that the hardware fundamentally doesn’t care about and that only cause programmers grief. In short, and feel more honest in either their rawness or their willingness to abstract a bit more in ways I actually find productive.

Plan9 Lives on

…in assembly, at least. I took the time to understand a bit more how llama.cpp worked and after a few SIMD optimisations in some of the more impactful matmuls, see if I could port some of that across to go-pherence for kicks. The fact that I can still consistently get 10% localised speedups by just looking at the inference graphs and asking “why wasn’t this done this way” (as you can see in my llama.cpp fork) makes me wonder exactly how much performance is being left on the table, and if Kaizen shouldn’t be a software design technique.

Lacking GPU hardware, I have to stick to stubbornly wrapping my remaining neurons around Intel-specific (and, soon, NEON-specific) optimisations, but it seems that my sparked a new interest in making inference go brrrrrrr I can’t quite turn off.

Side Effects

Speaking of turn-offs, the AI agent/shell/UX craze is still raging on to the point where I decided to put piclaw on hold for a bit and prepare the way to converge with what I fervently hope will be a thoughtful, elegant agent harness that is emerging from the SDK.

I am, effectively, taking a gamble on what it might look like, and simultaneously preparing to clean up piclaw’s event loop into less of a mess by rebuilding part of the internals into more or less cleanly cut “effectors” that run after each state machine transition as needed.

I also started putting hard caps on my “AI time”. Either token-wise, or attention-wise, I am taking advantage of the summer season to just go slower on a few projects.

No More ArchiveBox

Except, of course, that I decided to add one more, but for a good cause.

I’m also trying to simplify the amount of stuff I run in general, and scoring it by a thoroughly unscientific ratio of actual use against the headaches each service causes. This week ArchiveBox’s number came up because it just keeps getting stuck in CPU loops, and having a machine subtly overheat over many hours in the middle of summer is not something I favour.

Plus the usefulness of the thing is also in question, really.

There is a long history of “read later”/maybe useful/procrastination in reading here that probably needs its own blog post, and I’m old enough to have gone through Delicious, Instapaper, Pocket, and now ArchiveBox.

I think the common thread between all of them (and the reason “read later” services exist) is the (mostly fake) reassurance that I will a) really read something later (which often doesn’t happen) or b) be able to find something again if I ever need it.

Both of those prey on human nature, such as it is, in a weird reflection of what we’re going through now with AI, since all of these bookmarking and archival “helpers” boil down into CPU cycles, storage and a slightly guilty sense of having outsourced remembering things to a machine.

And I really should know better, since this very site has mostly replaced all of them for many uses–just go into any technical wiki page and it’s usually crammed full of links to interesting projects in various domains. And it just works.

ArchiveBox, meanwhile, has been a random source of failures, CPU use and frustration around its pace of development. Although I like the idea of storing bits of the web before they’re eroded away by slop, there are two fundamental problems with it:

  • It has too many moving parts
  • I don’t need to keep a zillion formats that I never open

What I need is to be able to find and read something on iOS when I eventually need it, and more or less high-fidelity content capture–for which MHTML is decent enough, even if Safari has never adopted it. Being able to strip it down into machine-readable or printable formats is mostly optional, and can be done on demand.

So… I whipped up bun-packrat, which does the entire thing relatively tidily, storing a single source of truth for the page contents, reshuffling it on the fly for me to read, and leveraging ’s FTS5 indexing for search. After all, with agents to extract text, fill in missing metadata and build whatever enrichments I end up wanting later, that’s more than good enough, simpler to maintain and far less wasteful (if you ignore my firing up a headless browser for every capture, of course). Instead of 35GB of junk, the 2.000-odd ArchiveBox entries now take up a little over 1GB in a single database, which is just great.

Notes for August 3-8

I have a problem. Or, rather, I have two problems:

  • I have been spending far too much time messing about with instead of reading.
  • I end up zoning out in front of YouTube and, even with serious attempts at curating my feed, I spend far more time than I should there.

I’ve , but this week I decided to take some time to fine-tune the friction around those activities–removing or adding it as needed.

E-Ink and Calibre MCP

My library has grown somewhat out of hand, either due to automated additions (I have been getting the web edition of The Economist delivered through it for ) or lack of proper curation, so I decided to use to see if I could remove the friction involved in finding something to read (or re-read) and have it either e-mailed to my via or easily accessible on my .

That mostly meant figuring out how to get an server working with , but it ended up including a parallel side quest to improve bun-readlater-epub and bun-opds-server to see if I could make it easier to queue up and download long-form content.

As an aside, recent revisions of CrossPoint have a Wi-Fi bug that prevents the from joining our Wi-Fi (apparently because it fails to use a coherent MAC address), so I have to import things via its hotspot (which automatically optimises the ). I suspect I will eventually have to fix it myself when I have more time, because the maintainers have been unable to do it for two releases now…

The real improvement, though, was how I decided to design my own MCP server for automating . Most existing implementations out there are either sidecars that can’t manipulate data or CLI wrappers that inevitably contend with access to its library databases, so I took the plugin route and built one that runs inside it and can thus use everything: metadata editing, conversions, moving books between libraries, e-mailing them to me, the works:

uMCP Bridge in Calibre
uMCP Bridge in Calibre

This effectively means I can now point any AI agent to and tell it literally:

  • find me the last three books by X and e-mail them to my Kindle address
  • grab this URL and import it into the Incoming library
  • change ratings programmatically
  • find and remove duplicates across libraries…

There’s no practical limit, really, although I might need to hack a few more things in. And since it uses the native job engine, everything works correctly.

The biggest quality-of-life improvement is that I don’t need to access the UI anymore. Since I have been running the extra-clunky Selkies-based container (which streams the desktop using its own thing and works very poorly on the iPad), there’s no more browser logins, slow wading through listings, or finicky right-clicking.

As an encore, since I wanted to maximise the time I could spend on the , I also updated picoflux and tweaked the e-ink theme I bolted on a month or so ago.

Old-Timey TV Shenanigans

That removed a lot of the friction around reading. Next, I needed to add friction to watching YouTube videos.

I recently spent a little while fiddling with Jellyfin–partly because I do that on occasion to see if it’s finally a decent alternative to (spoiler: not yet, by miles), but also due to my infrequent investigations into the state of hardware video decoding (go-264 will eventually go down that path).

This time I wanted to hack it into a sort of anti-YouTube setup and see if I could replicate the old-timey, “synchronous” TV-watching experience, so I literally told piclaw:

I want you to create a Debian 13 LXC in radxax4 to host Jellyfin, mounting smb://zfs/video as a read-only remote media library (so keep the LXC filesystem small, please). You can pick the best way to deploy Jellyfin as long as you manage to get hardware video processing working and document it in Obsidian. After that is done install jellyfin-plugin-livechannels or a similar TV simulation channel and create appropriate “channels” based on the contents of the media library.

Fifteen minutes later, I had the entire thing working. Not half-assed or poorly installed, working, with the media mounted read-only (twice, once on the host and again inside the LXC), QSV hardware transcoding and a full installation and configuration brief in (the output is a tad long, but here’s the last part):

Jellyfin installation and hardware transcoding summary
Jellyfin installation and hardware transcoding summary

Of course Jellyfin immediately filled the filesystem with artwork and people metadata, so that was (automatically) upgraded, but hey, it was a valiant effort.

Then I went to town on the original plugin and decided to bolt on Invidious as a YouTube source:

Jellyfin LiveTube channel guide on a television
Jellyfin LiveTube channel guide on a television

I call it jellyfin-livetube, and it’s been working pretty well–even the standard library-backed channels have been a surprising amount of fun (I ended up rewatching Flash Gordon purely by chance).

wrdp’s 15 Seconds of Fame

Speaking of YouTube, Wendell Wilson (hi there!) featured my new wrdp project in the latest Linux weekly video, which was fun to see (especially since I found out over breakfast while checking my messages, before coffee kicked in).

Right now it is mostly working, even though I am still waiting on a critical PR to make it into IronRDP to improve internal session management. But login and Wayland session creation both work, it’s all hardware-accelerated (both EGL desktop acceleration and VA-API video encoding), and… there are all sorts of edge cases to sort through.

I will be poking at it irregularly over the next few weeks–I want to do ARM/Vulkan acceleration next and set up two separate remote desktops I use “regularly”, but first I need a bit of digital detoxing, which is something I am very much failing at.

Moar Agents! Minis Agents! Moar

Because I was clearly left unsupervised and can’t really stop doing agent-related stuff, I went down the rabbit hole of setting up on my iPad Pro, and I did it in the most “me” way possible–I got it to connect to Memento and “learn” from the accumulated knowledge of all my piclaw agents, which is awesome because it proves two points:

  • Memento works perfectly for the purpose it was designed for.
  • It doesn’t matter which agent harness you use as long as you document things properly.

Thirty minutes after I began, I already had most of the SKILL.md files and scripts ported across, and an hour later I had “native” equivalents to manage and and was doing various shenanigans to generate and post screenshots along with my link post drafts, which these days are posted using the GitHub API directly (again, refactoring an existing SKILL.md).

I can now confirm that is, indeed, the Siri I have been wishing for. It’s by no means perfect, but with a SOTA model driving it, it can build its own tools, and… I really should stop now and go read a book or something.

3D Printing State of the Union

Since I got and I’ve been documenting my endeavours in this realm in a rather haphazard way, I thought it would be useful to do a sort of catch-up/snapshot of where things are (and have been) for a while now.

Read More...

Notes for July 26-August 2

Well, this was a productive week… end. The week as a whole was not particularly inspiring or meaningful (I won an award of sorts at work, and to be honest I… just wanted to get to Friday), but I finally teed up and actually did a bunch of things I’ve been meaning to, and it feels great.

Read More...

Twenty-Four Years Of Wiki Engines

Yesterday morning I switched this site to its fourth major engine, a little over twenty years after sketching out the core idea behind it. The new generator is called , is written in , and runs almost entirely inside GitHub Actions–which is mildly amusing because the first , in 2006, was also meant to batch-render this same filesystem tree using dependency tracking.

Read More...

The Beelink Mate Pro

Even though I already have probably too many desks (my main work desk, a that has probably been improvised for far too long, and an occasional tactical take-over of our dining table), I spend around a third of my working hours on a laptop of some kind.

Read More...

Archives3D Site Map