#1 — Why Polling, Not Webhooks
The first architectural decision. Everything else follows from this.
The Question
When you want to react to GitHub events, there are two obvious approaches:
- Webhooks — GitHub pushes events to your endpoint
- Polling — you periodically ask GitHub "what's new?"
Every serious integration uses webhooks. It's real-time, it's efficient, it's the "right" way. So why did we choose polling?
The Problem with Webhooks
Webhooks need infrastructure:
- A public URL (or ngrok, or a tunnel)
- SSL certificates
- Signature validation
- Retry handling for missed deliveries
- A web server that's always running and reachable
For a tool that's supposed to run on a laptop or a Raspberry Pi, that's a lot of overhead. The whole point of autosolve is "just run and forget." Webhooks are the opposite of that — they're "set up infrastructure and maintain it."
Why Polling Works
Polling is dumb, and that's its strength:
- No public endpoint — runs behind any firewall, NAT, VPN
- No infrastructure — no DNS, no SSL, no reverse proxy
- Crash-safe — missed a cycle? Next one catches up. State is in SQLite.
- Deployable anywhere — laptop, server, Raspberry Pi, Docker, systemd
The trade-off is latency. Webhooks react in seconds, polling reacts in minutes. For AI agent dispatch, minutes is fine. Nobody expects an AI to fix their bug in real-time.
The Cursor Pattern
To make polling reliable, we use a sync cursor — a bookmark that tracks where we left off:
sincetimestamp — only fetch issues updated after this pointpagenumber — for paginating through large result setsETag— GitHub's conditional request header, returns 304 when nothing changed
The cursor is persisted in SQLite per repository. After a restart, polling resumes exactly where it stopped. No duplicate processing, no missed events.
What We Learned
Polling is unsexy but operationally simple. The complexity budget we saved on infrastructure went into making the polling loop robust — retry, backoff, rate limiting, cursor persistence. That turned out to be a much better investment.
Hindsight
We might add an optional webhook receiver later (it's on the Ideas list). But polling will always be the default — it's the zero-config path.
Commits: 0110a08 init → 65dcbf0 first issue polling