The cron run that started at 6:30 AM took 894.91 seconds, cost 735 credits, and ended with an EOF from the vault connector when it tried to deliver its findings. The daily notes never landed in the vault. The credits were gone anyway.

That was the daily agent on a credit-metered hosted platform. A scheduled task scans HN, a few subreddits, X and vendor blogs for the last 24 hours, picks 5 to 8 items, and drops one markdown file into our shared vault through a connector tool. At 735 credits a run, a single day of it takes a big bite out of a prepaid balance that also has to cover everything else I hand that agent.

The run I kicked off anyway

That evening I was working through my partner’s latest update on our side venture. We trade updates through a shared git repo, and each of us has agents that read and write it. At 9:01 PM I pointed a task on the metered platform at the process question. It ran 145.6 seconds, burned 39 credits, and the log records one word: (error). A second, unmetered coding agent took up the same question at 9:02 PM and answered in 25 seconds on 11,234 tokens. By 9:31 my own log line says the metered agent was out of credits and the second one had covered.

So the wrong turn was treating the metered agent as the default and the others as the fallback. I learned the balance was empty because a job died, which is a bad way to read a balance. An error with no number attached looks the same as a flaky connector, and I had just watched that same connector throw an EOF that morning. I spent 39 credits finding out, on top of the 735 that bought nothing. I filed a ticket for an overdraft guard that checks the balance before dispatching, because nothing in the loop was doing that.

The daily job was a poll

Looking at the two failures side by side, the daily job was a poll. Every morning a model wakes up, reads the internet, and burns credits whether or not anything changed. The thing I needed most that night was not news. I needed to know the moment my partner pushed to the vault, because the work moves by baton: he pushes, I read, I reply, the baton flips. Notifications ran through DMs from a shared platform bot. I built a dedicated Discord bot for the vault instead.

The shape of it:

  • One channel per person, so my pushes and his pushes never share a feed.
  • A check every 5 minutes against the vault repo.
  • If the remote head differs from the last one it announced, post the new commit subjects and author to that person’s channel.
  • No model anywhere in the loop.

The core is about this much shell:

Terminal window
git fetch -q origin
new=$(git rev-parse origin/main)
old=$(cat .last_posted 2>/dev/null)
if [ "$new" != "$old" ]; then
git log --format='%an: %s' "${old:-$new}..$new" | post_to_channel
echo "$new" > .last_posted
fi

That runs 288 times a day and costs zero tokens. It fires only when there is something to say, which is the opposite of the daily agent, which always fires and sometimes has something to say. I verified it end to end with a real push while the session was still open. The only loose end was that my partner still needed a server invite to pick up his side. Until he joins, the channel for his pushes is a bot talking to me.

What the shutdown cost

I disabled the daily cron at 10:51 PM, so the news scan is off. Nothing replaced it. The scan’s top item that morning, a Claude Code release that stopped running its verify and code-review skills on its own, was a real find, and it was sitting in a file that never reached the vault. If I want that job back, it will come back with a balance check in front of it.

The watcher handles the part of the job that was never a reasoning problem. “Did a file change in this repo” is a comparison of two hashes. I had been paying a model to poll for it, and later to fail at it, and the credit meter only told me when it was already too late.

Later that night I hit a different bug, a phone remote-control session that registered with its host service and died seconds later. Same reflex. When a job fails silently or with a bare error string, I no longer assume the model is the thing that has to fix it. First I ask whether the model needed to be in the loop at all.