Covers May - early June 2026.

Through April, aidd was basically a capable pile of shell scripts wrapped around other people’s CLIs, while most of my attention went to the template and the fleet. In May that flipped. The template got sealed, and aidd got rebuilt as v2: a pure TypeScript/Bun runtime with a real web control panel. Most of the month went into keeping runs alive and making failures easier to diagnose.

On May 5, I tagged Spernakit v3.8.0-lts, with patch-only changes planned from there. The last stretch of the dance was producing mostly polish, and I wanted to get back to building applications.

Since I don’t entirely trust myself here, the guard rails got built to stop me specifically: hard fails on dependency drift, lockfile changes, and manifest staleness. Will the freeze hold? I honestly don’t know. I’ve already felt the itch. Ask me in a month.

I got sidetracked first by AgentWatch, a read-only scanner that inventories what my AI agents can actually reach: remote endpoints, broad permissions, credential paths, dynamic tools. These agents have quietly picked up a lot of access over the months, and I wanted to actually see it laid out.

aidd v2 came up alongside v1 as a parallel implementation, and the cutover went carefully: parity measured against a frozen v1 reference, defaults kept boring and easy to reverse if validation failed. By May 15, aidd was dogfooding itself - a full audit cycle on its own repo turned up a few dozen findings.

That run also exposed the first genuinely interesting orchestration bug. The Director had learned to propose fleet-level maintenance suggestions, and coding runs started treating those suggestions as coding work, wandering off the feature backlog to chase advisory maintenance. The fix was making the boundary explicit: coding mode selects feature-backed backlog work, and Director priority is advice, not a command. What I liked about the episode is that the system surfaced the problem through its own UI, and the fix went into its own rules.

Late May saw the feature set expand quickly - audit management, invocation telemetry, an MCP server, a Telegram bridge to the Director, plus a pile of smaller pieces. The big one landed May 22: runs detached from the web process lifetime. The CLI owns the run and writes heartbeats; the panel reconciles state from what’s on disk. You can start a run from any surface and the panel will find it.

Windows had opinions about that.

Windows process semantics

Four separate problems. I proved each fix with a minimal reproduction before I trusted it, because Windows has burned me before:

  1. taskkill /F delivers no catchable signal, so graceful shutdown can’t exist unless you build it yourself. Hence a POST /admin/shutdown endpoint, so the process can be asked politely from the outside.

  2. Node’s child_process.spawn lets children inherit the parent’s HTTP listen socket. A launched app pinned aidd’s own port; after a restart, the port stayed LISTENING, attributed to a dead PID, until reboot. Every child of the web backend now spawns through Bun.spawn, which restricts inherited handles. This class of bug appeared three times before I made the fix structural instead of local.

  3. Bun places children in the parent’s job object, and parent exit kills the job. unref() doesn’t help. So a “detached” run died the instant the panel restarted - proven with a repro where the child wrote zero lines and died with an empty stderr. The fix is ugly but it holds: a transient hidden PowerShell bridge (Start-Process) breaks the new process out of the job object, the real argv travels in a JSON payload file so user arguments can’t inject shell metacharacters, and the run’s PID gets reconciled from its first heartbeat.

  4. A healthy hour-long run and a hung one look identical without liveness data, which caused a real misdiagnosis during beta. Runs now persist the heartbeat age and show a green/amber/red dot so I can stop guessing.

The capture below is from a later run - August 2026, not June - because green is the only one of the three states a healthy run will hold still for.

A running audit in the panel’s run list: a green dot beside the word Live and the current activity, above the run’s backend, model, and reasoning-effort badges.
The heartbeat updates every five seconds. Green shows a recent update; amber appears when updates stop. The panel reaps the record at the red threshold, which makes that state difficult to capture.
Four Windows process bugs and the fix each one needed: no catchable kill signal, an inherited listen socket, a job object that killed detached children, and a hung run indistinguishable from a healthy one WHAT BROKE WHAT FIXED IT 1 taskkill /F delivers no catchable signal a POST /admin/shutdown endpoint 2 a child inherited the HTTP listen socket the port stayed LISTENING on a dead PID every child spawns through Bun.spawn third sighting; the fix went structural 3 parent exit killed the job object and the detached run - no output a hidden Start-Process bridge argv in JSON; PID from first heartbeat 4 a hung run and a healthy hour-long one looked identical from the outside persisted heartbeat age, shown as a dot
Every fix on the right started as a minimal reproduction. The second one is the one I shipped three times before making it structural.

None of which was trivial to hunt down.

Rooting out “done”

This past week produced my favorite little saga in the changelog. Feature badges were rendering gray for features marked done. The quick fix - alias done to green - actually shipped, and got reverted the next release once I realized it was the wrong fix. completed is the canonical status; done was invalid data written by an audit prompt that had taught the agents the wrong vocabulary. So the correction went to the source: the prompt got fixed, the feature store now refuses non-canonical statuses outright, and invalid data renders red instead of quietly passing as green.

The same push added a pile of smaller guards I won’t itemize. There are two I’d point at: the database now checks its own integrity on every startup (its first pass found and repaired some old broken references, so that paid for itself immediately), and the coordinator got failure-reason recording one release before it got auto-cycling - deliberately in that order, because I didn’t want to automate a cycle whose failures weren’t even being recorded yet.

Everything aidd has ever touched is a project I created, with a contract I wrote, and that bothers me more the longer I look at it. Pointing it at a codebase it didn’t write and having it learn the project - analyze, profile, inventory, audit - without editing a line of the app’s own code is the part I have no evidence for yet. I’m looking forward to it, though.