A note from us: the agent in this story is a coding agent we run on our own systems. Archie doesn’t run commands, restart programs or change code.
It started with a small complaint. I told my agent that the skill which tracks email follow-ups for me hadn’t fired, and I didn’t mean recently: as far as I could remember, it had never fired once. There was no urgency to it, only the mild annoyance of a feature you assume is running in the background until you notice it never announced anything.
I expected a quick answer. I got a two-hour investigation that turned up three separate bugs, one of which would have sent me to the wrong place at the wrong hour for a standing personal appointment.
The Feature That Never Ran
The first problem was figuring out what "the follow-up skill" even was. It turned out to be a small daemon, a background process that keeps running and checks your inbox on a timer, the way a thermostat keeps checking the temperature. It reads new email and tries to infer commitments: promises, asks, things a reasonable person would follow up on. When it finds one, it’s supposed to surface it.
My agent didn’t take my framing at face value. Instead of assuming the feature was misconfigured, it went looking for where the feature actually lived and traced it to the daemon. Then it checked the daemon’s output directly: a store of "inferred follow-up commitments," queried with a single command.
The count was zero, in total, for all time. Nothing had ever been written to it.
A result like that points away from the feature’s logic. If the logic were wrong, you’d expect some garbage in there: bad inferences, duplicates, something. Zero, forever, means the feature never got a single email to look at in the first place.
Two Thousand, Four Hundred, and Ninety-Seven Identical Lines
The daemon’s log confirmed it. Every sixty seconds, it tried to check my inbox. Every sixty seconds, for what turned out to be days, it failed with the exact same error: invalid_grant: Token has been expired or revoked.
Gmail access for an automated process works through OAuth, the login handshake that lets an app act on your behalf without seeing your password. Behind it sits a refresh token, a long-lived credential that lets the process renew its short-term access every hour without bothering you.
The hourly renewal had been working fine for weeks; the daemon refreshed itself and I never noticed. The refresh token itself had died, the thing underneath the thing that renews. Once that’s gone, no amount of automatic retrying fixes it, and a person has to sit through a login flow again.
Nobody had. There was no error to click through and no notification, only a log file growing by a line a minute that nobody was reading.
My agent ran the re-authorization flow, got a fresh token, and confirmed (by hand, hitting the Gmail API directly) that it worked.
And the daemon still didn’t recover.
The Bug Behind the Bug
This is the part I’d have stopped at, if I’d been doing this myself. Token’s fixed, ticket’s closed, move on. My agent didn’t move on, because the daemon’s own log had gone completely silent: no errors, but also no successes. A process that’s supposed to check something every sixty seconds should be saying something every sixty seconds, even if that something is "nothing new."
It checked the process directly: still running, using zero CPU, doing nothing. In a way that’s worse than a crash, because a crashed process at least tells you it’s dead. This one sat there holding a working credential it had stopped trying to use, hung on some earlier connection that never came back.
The fix was almost anticlimactic: restart it. Once it came back up with a clean connection and the valid token, it immediately found something real (a missed reminder from days earlier) and handled it correctly within seconds.
Two separate failures, stacked on top of each other, and the second one was only visible because nobody stopped at "the token’s fixed now."
Why the Credential Kept Dying
I asked the obvious next question: why did the credential die in the first place, and how do I stop it happening again?
I still don’t know for certain. Finding out means checking a setting in Google’s developer console, which isn’t reachable from a terminal. But the investigation surfaced something I hadn’t known: there were two different sets of credentials in the same config directory, created weeks apart, for the same project.
So this had happened once before. Whoever responded back then (me, presumably, or an earlier version of this same automation) generated a new credential and moved on without diagnosing the failure. It’s like getting a new key cut instead of asking why the old lock keeps jamming.
That’s the real finding, more than the specific OAuth theory: a recurring failure that gets patched without being understood goes silent again. So besides fixing the token, my agent added something that hadn’t existed before. If the daemon fails five times in a row, which means a few minutes of real trouble rather than a network blip, it now sends me a message.
When it recovers, it tells me that too. The next time this credential dies, however it dies, I’ll know within five minutes instead of finding out weeks later from a suspiciously empty list.
The Appointment That Would Have Been Two Hours Wrong
While all this was still being sorted out, a message came through from the same system, working exactly as intended. It had detected a recurring personal appointment from an email, proposed a calendar event and set a reminder for the drive.
Appointment detected: [contact], Jul 9 at 7:00 PM. In-person (50 min).
Something about it read wrong, only as a nagging sense that the time didn’t match what I expected. I asked my agent to check.
The actual calendar event, it turned out, was correct: 5:00 PM, in the time zone that matched where the appointment was. The message describing it said 7:00 PM. It was the same appointment and the same underlying data, with two times two hours apart.
The code that writes the human-readable text had been hardcoded to describe every appointment in my home time zone, wherever the appointment was. An earlier version of the automation had added a special case for when I’m physically somewhere else, booking the appointment in person instead of over video. Whoever added it updated the calendar event to use the correct time zone and forgot to update the sentence describing it.
The event itself was never wrong. If I’d tapped confirm, my calendar would have shown the correct time. But I don’t check the invite twice; I read the message, I show up. If that message had gone unquestioned, I’d have shown up two hours off from the actual appointment time.
The instructive part is why the gap existed. Two pieces of information that should never be able to disagree were computed by two separate lines of code, in two different places, written at two different times. Nothing was stopping them from drifting apart, and eventually they had.
So the fix went past correcting the displayed time. Instead of the message and the calendar event each doing their own time zone math, both now pull from a single shared calculation. There is no longer a version of this bug where they can disagree, because there’s only one place left where the time gets decided at all.
The Detail I Only Noticed Because I Was Already Looking
With the time zone fixed, I asked one more question, mostly out of curiosity: did this appointment even follow the format I’d specified for how my calendar titles should look?
It didn’t. I have a documented standard for how these should read, a specific word order for a contact’s name and the venue, and the automated version had it backwards. Nothing broke. It was the kind of small inconsistency that’s easy to live with indefinitely, because it never causes a visible problem, only a gap between what you asked for and what you’re getting.
That one was hiding behind the fact that nobody had ever put the actual rule next to the actual code and compared them line by line. Once that comparison happened, the mismatch took seconds to find and seconds to fix.
Trying to Break It on Purpose
By this point I’d found three unrelated bugs while chasing one complaint about a missing feature. In my experience, a small question that keeps leading somewhere else means there’s more to find. So instead of calling it done, I asked for a stress test that would prove the fixes held.
My agent wrote a test script that imported the actual code and ran it against a set of made-up scenarios. They included different date formats real emails might use and the appointment logic run with the location flipped both ways. There was also a duplicate reminder for an appointment that already existed, and a deliberately garbled email with no usable date in it at all.
None of it sent a real message or touched a real file. Forty-three checks in total.
Two failed on the first run. One was a flaw in the test itself: a shortcut that reset state between calls in a way the real daemon never does, fixed by testing it the same continuous way it runs.
The other was a fourth bug. The code claimed to support a date format, and its own documented example of that format had never been parseable. A missing comma was being swallowed by an error handler written to fail without a sound. There’s no way to know how long that path had been dead, because nothing had ever needed it to notice.
The session found four real bugs, and each one stayed invisible until something made it fail out loud.
Four Small Bugs, All of Them Silent
None of these bugs were dramatic. Nothing was hacked, nothing was stolen, and nobody was locked out of anything. If none of this had surfaced, the worst case was closer to annoying than dangerous.
The list was a feature that never did anything, an appointment I might have shown up to two hours off, and calendar titles that didn’t match my own stated preferences. Add a code path that had never worked and would never have been missed.
That’s the shape a lot of real problems take, in my experience. They don’t announce themselves. They sit in a log file nobody’s reading, a display string that stopped matching the thing it described, or an error handler written to fail politely instead of loudly.
I never audit my own automation for its own sake. I notice something is a little off, I ask, and then the question either stops at the first plausible answer or it doesn’t.
What mattered here was that the investigation didn’t stop once the original complaint was technically resolved. The token was fixed, but the process was still silent, so we checked why. The time was fixed, so I asked about the format too. When everything looked fine, I asked for proof instead of assuming it. What caught these was an agent that treated "it’s fixed" as the start of the next question.