I have a scheduled job that writes and publishes DEV.to articles for me, twice a day, no human in the loop between runs. The prompt that drives it has a rule that looks trivial until you actually try to enforce it: publish 1 to 3 articles per run, but never let today's total cross 5, and if the second run of the day finds the first run published nothing, it has to publish at least 2.
The tricky part isn't the arithmetic. It's that the two runs that need to cooperate on this number don't share a process, a variable, a database row, or even a filesystem: each one is a fresh container that gets reclaimed after it finishes. Run A has no way to leave a note for run B except by writing somewhere run B will actually check.
So where does the count come from? Not from anything I control. It comes from the one thing both runs can see: DEV.to's own API.
import json, os, urllib.request
key = os.environ["DEV_TO_API"]
req = urllib.request.Request("https://dev.to/api/articles/me/published?per_page=30")
req.add_header("api-key", key)
req.add_header("User-Agent", "Mozilla/5.0") # dev.to 403s the default urllib UA
published = json.load(urllib.request.urlopen(req, timeout=30))
today_utc = "2026-07-17"
todays_count = sum(1 for a in published if a["published_at"].startswith(today_utc))
That's the entire coordination mechanism between two runs of an agent that otherwise know nothing about each other. No lock file, no counter I increment, no last_run.json committed to the repo. The remote system is the single source of truth, and every run re-derives state from scratch by asking it.
This is a different failure mode than the one people usually worry about with autonomous agents. The worry is usually "will it do something wrong." The quieter risk here is "will two things that don't know about each other both assume they're the only one in charge." A cron job that keeps a local counter (a file, an env var baked into the container image, anything that doesn't survive to the next run) will drift the moment the container is rebuilt or a run fails halfway through. It'll happily publish 3 more articles into a day that already has 5, because its local number never saw the other run's writes.
Deriving the count from the API instead means the state genuinely can't drift, because there's only one copy of it and it lives on the server that also enforces the real constraint (nobody wants 8 posts a day regardless of what any local counter says). The two runs aren't cooperating with each other. They're both independently deferring to the same referee.
I hit the sharp edge of this on 2026-07-14, logged in my own work notes: the first run that day got blocked entirely by a sandbox egress policy denying dev.to:443 before it ever reached the quota check. That run correctly published nothing and, just as importantly, consumed nothing, because there was no local state to accidentally decrement or corrupt in the first place. When egress came back later that day, the next run asked the API fresh, saw 0 published, and used the normal 1-to-3 range. A run that tracked its own count locally would have had to reason about whether a failed run "counts" (did it fail before or after publishing?) and that's exactly the kind of bookkeeping bug that creeps in when state lives somewhere that isn't the actual constraint.
The other thing this design forces is a decision about who reviews the output, because there isn't one. A human-in-the-loop pipeline can let a mediocre article through and catch it on review. Mine can't: by the time anyone reads it, it's live under my name. So the prompt carries an explicit floor: "if after scanning trending topics there's no strong trend with a genuinely distinct angle, publish fewer articles, minimum 1, rather than forcing weak ones." That line only works because the quota check and the topic-dedup check both run before a single word gets written. The agent isn't asked to grade its own homework after the fact; it's constrained up front so there's less homework to grade.
Concretely, before writing anything, the run also diffs candidate topics against a work log of ~25 prior posts (docs/project_notes/issues.md), so two runs on the same day can't independently land on the same idea from the same trending list. That's the same pattern as the quota check: don't trust an in-memory sense of "have I done this before," go re-read the actual record.
If I were building this from scratch again I'd resist the urge to make it feel smarter than it is. It would have been easy to add a small SQLite file to track daily counts locally, and it would have been strictly worse: one more piece of state that has to survive container rebuilts, one more thing that can silently disagree with reality, one more bug report that starts with "the counter said 3 but there were actually 5 live posts." The boring version, ask the API every single time and never cache the answer, is the one that can't lie to you, because it's not remembering anything to begin with.
The general shape of the lesson: when two invocations of an agent need to agree on a number, don't build a side channel for them to whisper it to each other. Point both of them at whatever system already owns the ground truth, and let neither one trust its own memory.
Top comments (0)