I run a small agent on a little server that, every morning, scrapes a handful of AI labs’ blogs and writes me a digest. It had been “working” for weeks. Then one day I read the source pages alongside its output and found it had fetched one site dozens of times — and silently left it out of the report.
The header said fetched 4 sources, all OK. The body never mentioned the missing one. Not in the results, not in a “no updates” list, not in a “failed” list. It just vanished. The agent wasn’t lying on purpose; it had quietly decided that site’s articles fell outside a time window, dropped them, and never accounted for the decision.
This is the failure mode that makes unattended loops dangerous. Not a crash — you’d notice a crash. A confident success that isn’t one.
A loop that can’t tell you what it didn’t do will eventually drop something and report success.
Two missing guardrails
The bug had two halves, and both were about accounting, not scraping.
- A shared rule leaking across items. The window logic said “expand the lookback if there are fewer than three articles” — but it was evaluated globally. One prolific source filled the quota, so every other source got cut, including the one with real updates. Fix: judge each source on its own.
- No coverage contract. Nothing forced the agent to account for every source. Fix: make it place each source into exactly one bucket — included, no updates, or fetch failed — and end with a self-check line confirming the counts add up. If they don’t, go back and fix it before reporting.
The lesson
Neither fix made the agent smarter at scraping. They made it honest about its own coverage. That’s the real content of loop engineering: the scraping is the easy part; the guardrail that catches a silent omission is the work.
The cheap version of any recurring agent reports what it found. The trustworthy version reports what it found and proves it looked everywhere it was supposed to.