Everything After the Push
This site is now fully live: its own domain, its own certificate, and, as of this week, a deploy pipeline. Every push to the main branch installs dependencies, type-checks, runs the full test suite, builds all ~670 pages, and ships the result to the CDN. No manual upload step survives.
One manual step does survive, and it’s the one I kept on purpose: the push.
The pipeline automates everything after the decision
This repo has always had an unusual rule for a personal site: it’s the one public window onto a pile of work that is mostly private, so publishing is a human act. No automation here may push, deploy, or make anything visible on its own. Until this week the rule was easy to keep, because deploying was manual drudgery anyway.
The new pipeline looked, for about a minute, like a violation. It isn’t; it’s the rule finally stated cleanly. The workflow triggers only on a push to main, and pushing is something only I do, deliberately, after reading the diff. Everything downstream of that decision (check, test, build, upload) is mechanical, and mechanical work is exactly what should be automated. The workflow file says this in a comment at the top, mostly so a future agent doesn’t “helpfully” close the loop.
The part I like most: this repo’s test suite isn’t just unit tests. It carries a guard suite: a redaction scanner that fails the build if anything matching a never-public pattern lands in a tracked file, a link guard that refuses any project link not explicitly allow-listed, an accessibility floor over every built page. Those used to be tests I ran before deciding to push. Now they’re deploy gates: there is no path to the live site that doesn’t pass through them. 674 tests stand between a commit and the edge, and about a dozen of them exist specifically to stop me from publishing something I shouldn’t.
Meanwhile, the thing that builds the site got rebuilt
Most of this site’s recent features were built by the background fleet: the couple dozen coding agents that take turns on my repositories, a few at a time, while I’m away. That system went through its own architecture change this week, forced by a real failure, so it belongs in the record.
The failure first. Earlier this month the fleet hit the usage ceiling on its top-tier model mid-run. Not a transient rate limit: a hard cap, the kind that doesn’t clear until the usage window resets. Jobs sat blocked for hours. A few sessions spun in retry loops, faithfully re-asking a question whose answer was not going to change. Four batches of planned work died without a single commit, and the runner’s liveness check, which only asked “is the process alive?”, marked them done. Everything looked fine on the dashboard. Nothing was fine.
The fix wasn’t “get more capacity.” It was noticing that the fleet was spending its scarcest resource, top-tier reasoning, on work that mostly doesn’t need it. So the fleet is now two-phase. A planner session runs on the strongest model tier, reads the repository without touching it, and makes every judgment call: what’s worth building, whether anything is worth building at all, the architecture, the tests that earn their place. It writes all of that into one executable plan file and exits. A separate executor session, on a cheaper and faster tier, picks the plan up fresh and implements it exactly: no redesigns, no scope changes, and if it hits a decision the plan didn’t anticipate, it writes that up and exits with a clean tree rather than improvising. The supervisor staffs the two phases; the agents never promote themselves between them.
The economics are the point. Judgment is expensive and compounds; typing is cheap and parallelizes. Concentrating the top tier on plans means a capacity wall there can now stall planning (annoying) instead of stalling everything, which is what actually happened. And a plan file turns out to be a useful artifact in its own right: it’s reviewable, it goes stale honestly (a day-old plan gets replanned, not trusted), and it’s a written record of why the change was made, produced before the change existed.
The watchdog got a sequel
I wrote in Two Kinds of Stuck about the watchdog whose whole job is distinguishing “the world said wait” from “a human must decide”, and about the night its own recovery loop deadlocked the fleet. That post has a sequel now, because a new class of the same bug showed up wearing a disguise.
The capacity cap above didn’t just waste a night; it exposed a blind spot. The cap message arrived wearing the same generic “rate limited — wait and retry” prefix as a transient server hiccup, so the watchdog classified it as the recoverable kind and backoff-retried into the wall, indefinitely. Then a reworded variant of the message showed up inside running sessions, where the classifier never looked at all. Four wedged agents held every fleet slot for eight hours while the watchdog truthfully reported that nobody was stalled. Same shape as last time: not a crash, a misclassification. The supervisor’s model of the world was missing one state the world actually has.
Two fixes went in, and the pairing is the lesson. The targeted one: the classifier now knows a quota wall as its own kind of stop: you don’t retry it, you wait out the window, and in the meantime the supervisor can respawn planners on a different top-tier model under a marker that expires after a couple of hours, so the fallback can’t quietly become the default. That fix handles this message. It does nothing about the next reworded message, and there will be one.
So the class fix: a dead-man backstop in the supervisor. Any fleet agent that has produced nothing for an hour, in a state the classifier cannot name, is presumed wedged and reaped, and its slot actually frees. The old watchdog acted only on what it recognized, which meant every novel failure got the most generous interpretation by default. The new rule inverts that: being unrecognizable for too long is now itself a recognized state, and it’s treated as the bad one. Pattern-matching still does the precise work; the backstop just guarantees the blast radius of a pattern it’s never seen is one stale slot, not the whole fleet.
What “live” changes
Honestly? Mostly the stakes. The site was already public; now it has a real address, and the distance from “I decided” to “it’s on the internet” is one push and about two minutes of CI. That’s a milestone I’m glad to mark, and a small one. The interesting machinery is everything behind it: the fleet that builds the features, the split that keeps it building through a capacity wall, the watchdog that now survives messages it can’t read, and the guard suite that checks every byte on its way out the door.
The pipeline automates everything after the decision. The decision is still the job.