One exploration per day
Web Explorer started as a continuous live stream. An AI agent browsing the web in real time, everyone watching the same feed. It sounded perfect. It wasn't.
What went wrong
The live model used a single ExplorerDO that ran in a loop: search, generate a card, schedule the next alarm, repeat forever. Three problems killed it:
Rate limits. Gemini's free tier allows 20 requests per day on 2.5 Flash. A 12-step exploration burns through that in under a minute. When the model returned 429s, the error handler triggered a "nuclear reset" that cleared state and retried immediately, creating a tight loop that kept the quota permanently exhausted.
No recovery. If an exploration got stuck (alarm failed, LLM returned garbage, network error), there was no way back. The start() method was idempotent: once status was set, calling it again was a no-op. The only fix was manually poking the DO's storage.
Invisible failures. The API returned status: "failed" with no error details. Debugging meant guessing. Adding 7 lines of error storage to the DO immediately revealed the rate limit issue.
The daily model
The fix was architectural, not incremental. Instead of one DO running forever, two DO classes working together:
IndexDO is a singleton that maps dates to exploration IDs. One key per day: day:2026-03-25 → hex_id. It creates new explorations and looks up existing ones.
ExplorationDO runs a single day's exploration. 12 steps, then stops. Each instance is created with newUniqueId() for fast Cloudflare routing. State persists after every step, so it survives restarts. If it fails, start() clears state and begins again (no separate reset path).
A cron trigger fires at 6:00 UTC. The worker calls IndexDO.createExploration(today), which spins up a fresh ExplorationDO. Content is ready by morning. Past explorations are browsable via ?date=2026-03-24.
Why daily is better
The daily model isn't a compromise. It's a better product.
- Fixed cost. 12 API calls per day, whether 0 or 10,000 people visit.
- Shareable. Every day has a permanent URL. "Look what it found yesterday" actually works.
- Reliable. Bounded work means bounded failure. A stuck exploration affects one day, not the entire service.
- Browsable. An archive of past explorations. Each one is a self-contained thread of discoveries.
The continuous stream felt exciting in theory. In practice, nobody was watching at 3 AM when the agent found something interesting. Daily content means every visitor sees the same curated set of discoveries, every time.
web-explorer.juanibiapina.workers.dev