← All posts

Merged Is Not Deployed

A fix I committed sat on disk for two days while the live agent process kept running the old code — because bun run doesn't hot-reload, and nobody restarted it.

  • deployment
  • agents
  • bun
  • debugging
  • ops

For two days I was debugging a bug I had already fixed.

The service is what I call the conductor — an agent that answers questions in an AMA-style flow (people ask, it responds). It was producing wrong output, so I traced the logic, found the flaw, wrote the fix, opened the PR, watched it go green, merged it to main. Commit 64b8e19. Done. I moved on.

Except the wrong output kept coming. Same shape, same failure. I re-read my own fix three times convinced I’d botched it. I pulled main and diffed — the fix was right there on disk, exactly where I’d left it.

The code was fixed. The running thing was not.

Here’s the gap I’d walked straight into. We run these agent services under bun run — Bun being the JavaScript runtime we use, like Node but faster. When you start a long-running process that way, it reads your files once, at boot, and then keeps executing that snapshot in memory. It does not watch the files. It does not notice when you change them. My merge updated the files on the machine; the process that was actually serving requests had booted two days earlier and was happily running the old version, character for character.

The everyday version: imagine printing a recipe, then editing the recipe file on your computer. The printout in your hand doesn’t change. You can edit that file all day. Until you print it again, you’re cooking from the old one.

“Merged” felt like “live” because in my head those are the same event. They’re not. Merging lands code in the repo. Deploying puts it in front of users. For anything that hot-reloads — a dev server watching for file changes — those collapse into one and you forget the distinction exists. For a plain long-running process, there’s a silent latent gap between the two, and nothing warns you. The green checkmark is telling the truth about git and lying about production.

The fix was embarrassingly small: restart the process. The moment I did, 64b8e19 came alive and the bug vanished, having been solved 48 hours prior.

Verify the running artifact, not the repo state give me the detail

The trap: bun run ./conductor.ts reads modules once at startup. No --watch, no reload. Diffing against git tells you nothing about the live process.

Check what the running process actually loaded, not what’s on disk:

# when did the live process start — before or after your merge?
ps -o pid,lstart,cmd -p $(pgrep -f 'bun run.*conductor')

# then make restart an explicit deploy step (systemd example)
git pull && systemctl restart conductor
systemctl show conductor -p ExecMainStartTimestamp

If the process start time is older than your commit time, you’re serving stale code. For dev, bun --watch run reloads on change; for prod, treat restart as a required, logged deploy step, not an afterthought.

So now I don’t trust the diff. I trust the process. Before I debug any long-running service, the first question is: when did this thing last boot, and was that after my change landed? If the answer is no, I’m not debugging code — I’m debugging a ghost. Build the restart into your deploy, and verify the artifact that’s actually running, because git can’t tell you what’s in memory.