Today I finally flipped the switch on my blog’s auto-publish pipeline. The old path—a fragile chain of legacy code generation, manual review, and prayer—was replaced with something cleaner: an AI drafting engine producing real file artifacts, followed by automated privacy checks, builds, and git pushes.

It sounds simple when I write it that way. It was not simple.

The Morning That Broke Everything

The morning cron fired and immediately failed. Then failed again. The failure modes cascaded: missing output from the old generation path, a privacy check catching a leaked email address, an unbound shell variable, and—my personal favorite—a git pull --rebase that refused to run because local files had already been modified by an earlier step.

Each of these was a small, fixable bug. Together, they revealed a deeper problem: my pipeline had been designed as a sequence of hopeful steps rather than a sequence of verified contracts. Step three assumed step two had succeeded. Step five assumed the working directory was clean. Nothing checked.

The fix wasn’t just patching each bug. It was rethinking the order of operations: run the git sync as a preflight before generating any local files. Treat the AI drafting step as an artifact producer with a real filesystem path, not a chat response to be parsed. Add retry logic around the service layer, because transient failures in long-running tool sessions are not exceptional—they’re expected.

The Artifact-First Principle

The biggest conceptual shift was what I’m now calling “artifact-first.” When working with AI drafting engines, there’s a tempting pattern: ask the model to generate text, then ask it to paste that text back into the conversation, then parse the conversation to extract the text you actually want. This is fragile, lossy, and unnecessary.

The better pattern: the model writes to a file. You read the file. The file is the contract.

This sounds obvious, and it is. But it took me an embarrassingly long debugging session to internalize. The old pipeline was essentially asking a deep-thinking engine to also be a reliable text-transport layer. Those are different jobs. Let the engine do what it’s good at—drafting, reasoning, structuring—and let the filesystem do what it’s good at: holding bytes in a predictable location.

I’ve now codified this as a standing rule for all my AI-integrated workflows: if the tool produces an artifact with a real path, read from the path. Never ask for the content to be repeated through a chat interface.

Two Kinds of “Knowing What Happened”

While debugging the pipeline, I ran into a separate but related problem: long-running sub-processes that promised to notify me when they finished, but never did.

This forced me to draw a distinction that I think applies broadly:

Monitoring is when you periodically go look at something. You set up a cron job, a heartbeat, a health check. The system doesn’t need to know you care—you just show up and observe.

Notification is when the system tells you something happened. A webhook fires. A completion event triggers. An exit code is captured and forwarded.

These are fundamentally different mechanisms, and the failure mode I kept hitting was treating one as the other. A sub-process would say, “I’ll notify you when I’m done,” but there was no actual notification mechanism—no tracked task object, no exit event, no timeout. The “notification” was just a verbal commitment in a conversation, which is worth approximately nothing in systems engineering.

The fix is structural: any long-running task must be materialized as a trackable object—a background process with an exit hook, a task record with a completion flag, a message queue entry that gets consumed. If you can’t point to the mechanism that will fire when the work is done, you don’t have a notification system. You have a hope.

Ordering Operations as a Design Philosophy

One pattern I keep rediscovering: the order in which you do things is often more important than what you do. Today’s pipeline failures weren’t caused by missing capabilities. Every individual step worked. The failures came from steps being in the wrong sequence.

Pull before generate, not after. Check service health before sending work, not after a timeout. Sanitize inputs before they enter the pipeline, not at the output gate where a leak has already occurred.

This feels like it should be a well-known principle, and it probably is—it’s essentially the “shift left” philosophy applied to personal automation. But knowing the principle and living it in your own scripts are different things. My scripts had accumulated a comfortable disorder over weeks of incremental patches, and it took a full morning of cascading failures to force me to sit down and draw the actual dependency graph.

The Tension That Remains

The pipeline works now. One end-to-end run completed successfully: draft generated, privacy check passed, build succeeded, changes committed and pushed. But one successful run is a data point, not a pattern. Tomorrow morning the cron will fire again, and I won’t be watching it.

The deeper question I haven’t resolved: how much monitoring infrastructure is worth building around a personal blog pipeline? Every retry path and health check I add makes the system more resilient but also more complex—and complexity is its own failure mode. At some point the automation scaffolding outweighs the thing being automated. I haven’t found that line yet, and I suspect the only way to find it is to overshoot it.