The engineering companion to "The Teammate That Tells You It's Broken Before You Notice." Everything below is the shipped system, not a design doc.
Two paths, one agent
@pulse does two very different jobs, and the split is the whole design. One path is a deterministic watchdog that reacts to GitHub in real time and never touches an LLM. The other is an LLM writer that composes summaries on a schedule. They share a name and a database, nothing else.
Getting that boundary right is what makes the economics work: the thing that runs 24/7 (the watchdog) is free, and the thing that costs money (the writer) only runs a couple of times a day. When someone asks "what does an always-on AI monitor cost," the honest answer is that the always-on part isn't AI at all.
The alert path: webhook → filter → dedup → post
A GitHub App webhook hits POST /webhook/github. The chain from there:
- Verify. HMAC SHA-256 signature check with a constant-time compare. A bad signature gets a 403 and nothing else happens.
- Filter, hard and early. Only
workflow_runevents are considered; everything else is acked with a 200 and dropped. Of those, we only care about runs that havecompleted, with a conclusion offailureortimed_out, onmainormaster. Pull requests, pushes, feature-branch CI: all ignored at this gate. This is the "one thing it will interrupt you for" from the blog, expressed as a filter. - Deduplicate (the interesting part, below).
- Post. A Block Kit alert goes to the project's channel, resolved from the repo; if we can't map the repo to a channel, it falls back to a configured ops channel rather than vanishing.
The webhook handler acks fast and does the real work in ctx.waitUntil, per Workers' execution model. No LLM is involved anywhere in this path. Classifying a failed CI run and deciding whether to speak is pure, fast logic.
The dedup that buys the silence
Alerting is easy. Not being annoying is the actual engineering. Dedup lives in a per-repository Durable Object (WebhookStateObject), keyed by repo name, that remembers just enough recent state to answer one question: "should I actually speak about this?" Three layers:
- Delivery replay. GitHub retries deliveries, so a rolling window of the last ~100 delivery IDs drops anything we have already seen.
- State-change only. The object remembers whether the branch was last passing or failing. It alerts on the pass → fail transition and nothing else. A build that is still red an hour later is the same news, so it stays quiet. This single rule is what stops the smoke-detector-with-a-low-battery problem.
- Cooldown floor. A per-event-type cooldown (30 minutes) caps how often a thrashing pipeline can generate messages, even across transitions.
One deliberate choice: the object fails open. If it were ever unreachable, the alert fires anyway. A duplicate alert is a minor annoyance; a swallowed "your production branch is broken" is a real incident. We would rather be occasionally noisy than ever silent about the thing that matters.
The summary path: cron, batched and idempotent
The digests are the other half. Two cron schedules drive them: a daily summary at 9 a.m. ET and a weekly review Friday at 4 p.m. ET. Both run the same code path; they differ in lookback window (24 hours vs. 7 days) and the format the prompt asks for.
Two operational details worth stealing:
- Batched with a deadline. Active projects are processed a few at a time (3), balancing GitHub's rate limits against the Worker's CPU budget, with a wall-clock deadline that skips the remainder and alerts ops rather than letting a slow batch blow the limit silently.
- Idempotent via the database, not the Durable Object. A cron firing twice must not double-post. Idempotency here is a Postgres concern: a unique index on
(project_id, summary_date, summary_type)plus an insert that does nothing on conflict. The Durable Object handles webhook dedup; the database handles cron dedup. Different problems, different tools. (One honest caveat: we post to Slack and then record the summary, so a Worker dying in that narrow window can double-post. We chose that ordering deliberately, so a failed post retries next run rather than being marked done.)
Unlike the alert path, the summaries are LLM-generated (Sonnet), so their correctness rests on the prompt's "never make up data" instruction and on the structured data we feed the model, not on any structural guarantee. That difference, deterministic alert vs. generated prose, is exactly the seam the blog is honest about.
The GitHub App, briefly
Read access to the repos comes from a GitHub App, not a personal token. On demand we mint a short-lived RS256 JWT (issued 60 seconds in the past to tolerate clock drift, expiring in 10 minutes, GitHub's max), exchange it for an installation access token, and cache that token in memory for its ~1-hour life. It's read-only and scoped to our own organization's repositories. Client projects on other systems are, by design, out of scope.
Observability survives the bypass
Every alert attempt and every summary run writes a row to agent_runs: the webhook path logs as pulse / webhook, the cron path as pulse / cron. This matters specifically because the alert path skips the LLM. It would be easy to build a "free" watchdog that is also invisible, that produces no record of what it did or didn't do. Instead the deterministic path still logs, so the observability story from Episode 0 holds even for the code that never calls a model. A monitor you can't audit is its own kind of risk.
Building something like this, or wondering whether the pattern fits your stack?
Back to the non-technical story.
