Work on What Has Decayed
Hexagram 18 is a picture of worms bred in a vessel left standing too long. I came back to an autonomous content system I built and found exactly that — bugs that grew in the dark because no one was reading the logs. The talk I submitted to PyCon HK 2026 is the field report.
The 18th hexagram of the I-Ching is 蠱 (Gǔ). The character is a pictograph: worms (蟲) bred inside a vessel (皿) — bugs festering in a container that sat still too long. The standard translation calls it Work on What Has Been Spoiled. Decay that sets in through neglect, and the work of going back to repair it.
I picked it for a talk before I realized how literal it was.

The vessel I left standing
A while back I built an autonomous content pipeline — it ingests sources, rates them with an LLM, drafts on-brand summaries and images, and publishes around the clock. I'm the only human who runs it. "Autonomous" sounds like a finish line. You wire up the scheduler, the thing posts on its own, and you walk away.
Then you come back two months later and look inside the vessel.
What I found was twelve jobs stuck in_progress, the oldest 62 days old. Most of them were harmless — crashed transcription and generation jobs that sat in the table doing nothing but cluttering the dashboard. One was not. A news-refresh job had died while holding the row lock that every new refresh waits on, and for 41 hours nothing fresh came in. No error, no alert. The vessel had been collecting dead jobs for two months; only one of them was actually blocking the work.
The four ghosts
The talk walks through four of these. Each one is a small, reusable Python lesson, and every one of them grew in the dark.
The 41-hour lock. Eleven of the twelve stuck jobs were just clutter; only the news-refresh job blocked anything, because it alone held a lock. The lesson is to tell failures that block apart from failures that accumulate — they look identical in the dashboard and need opposite fixes. I reset the blocking path at acquisition time (any lock-holder older than a couple of hours gets failed) and swept the rest periodically. No heartbeat service, no new infrastructure.
The stale-fresh race. The publishing cycle called an async refresh that scheduled the work and returned immediately — so the next step selected content before the fresh news existed, and the system cheerfully published yesterday's data for a full three-hour cycle. await on a function that kicks off background work returns before the work is done. The fix was to chain generation off the refresh's completion callback instead of off asyncio.sleep and optimism.
The 3am datetime crash. TypeError: can't subtract offset-naive and offset-aware datetimes. Two patterns met in one subtraction — datetime.utcnow() in one file, datetime.now(timezone.utc) in another. This is the most on-theme bug of the four, because mixed datetime conventions are exactly what scattered, AI-written code produces when nobody enforces one pattern. Pick now(timezone.utc) end to end, and coerce the legacy naive values on read while you clean up.
Seeing in the dark. I needed to know what twenty-odd scattered LLM call sites were actually doing, without editing any of them. So I monkey-patched the call layer to trace every request — tokens, timing, model, straight to a log table. The whole safe move is one line of discipline: save the original method first, then call it explicitly inside your wrapper. Call the patched method by name and you recurse forever.
That last one, incidentally, is the same shape as the logging I just wired into this very site — every image I generate now writes its prompt and parameters to a shared ai_provider_requests table. You instrument the thing once, in one place, and then you can finally see.
Three days before, three days after
The Judgment for hexagram 18 has a line that stuck with me: three days before the turning point, three days after. You study what led to the decay before you act, and you tend the repair after — the work isn't the single fix, it's the attention on either side of it.
The Image is below the mountain, wind — wind that circles the base of a mountain and stirs up what has gone stale. None of these bugs needed a clever algorithm to find. They needed someone to come back, lift the lid, and look. I went in thinking autonomous meant I was done. It mostly meant I'd stopped reading the logs.
I submitted this as a 30-minute talk to PyCon HK 2026 — The 3am Failures: Lessons From a Solo-Operated Autonomous Content System. The conference is in November; I'll publish a sanitized snippet repo alongside the slides. If it gets accepted, the worms get a stage.