Skip to content

feat: generic RSS/Atom feed provider (fallback for unsupported status pages)#17

Open
anthonybaldwin wants to merge 6 commits into
mainfrom
feat/feed-provider
Open

feat: generic RSS/Atom feed provider (fallback for unsupported status pages)#17
anthonybaldwin wants to merge 6 commits into
mainfrom
feat/feed-provider

Conversation

@anthonybaldwin
Copy link
Copy Markdown
Owner

What

Adds a generic feed provider so any status page that publishes a direct Atom or RSS feed can be monitored — used as the last-resort fallback after the vendor providers (Statuspage, incident.io, Instatus). Motivated by Slack's bespoke slack-status.com, which isn't on any vendor platform but does publish /feed/atom and /feed/rss.

Why a feed (not a "slack" provider, not HTML scanning)

Research against live Slack + GitHub data (JSON API vs Atom vs RSS):

  • JSON APIs are richer but useless as a generic fallback — they only help when the vendor is already known (which is what the existing providers are for). The advertised feed is the only self-describing structured source on an unknown site.
  • Atom carries the full update history in <content> (a stable <id> + timestamped update blocks that accumulate in place as the incident progresses). This maps directly onto Squawk's existing dedup-by-id polling model — "one entry = one event we watch for new updates."
  • RSS is thinner and vendor-dependent (Slack RSS is summary-only), so Atom is preferred where available.

Per review discussion, discovery is not done by scraping a page's HTML. The user supplies a direct feed URL to /monitor add; that URL becomes the monitor's baseUrl. The feed provider probes last and only matches a parseable Atom/RSS document, so vendor pages always win first and a plain HTML page falls through to a reworded error pointing at the feed-URL path.

How it works

  • feed-text.ts — shared XML-text helpers (decodeEntities, plainText extracted from instatus.ts; new parseFeedTimestamp).
  • feed.tsdetectFeedKind (atom vs rss by content), parseFeedUpdates (timestamped blocks → stable-id IncidentUpdates; <strong>STATUS</strong> marker when present, prose keyword-sniff otherwise), parseAtomFeed/parseRssFeed, feedPageStatus, and the Provider.
  • Wiring: "feed" added to ProviderId + monitor schema enum; registered last in PROBE_ORDER; /monitor add error reworded.
  • Resolved is terminal (any resolved update ⇒ resolved, independent of newest-first vs oldest-first ordering). Impact always defaults to minor — feeds carry no impact severity.

Verification

  • bun test — 56 pass / 0 fail (new feed.test.ts, feed-text.test.ts, registry + feed-fallback detection tests). bun run typecheck clean.
  • Live-feed smoke test against the real endpoints:
    • Slack Atom: 49 incidents parsed, stable ids, resolved detection + multi-update ordering working.
    • GitHub Atom (Statuspage): 25 incidents, newest-first updates sorted ascending, the one genuinely-live incident correctly active.
    • Slack RSS: summary-only → one update per incident (documented thinness; Atom preferred).

⚠️ Known limitation worth your call (open question)

Live data shows 6 of 49 Slack incidents (~12%) linger as false-active: all genuinely resolved, but Slack closed them with prose that never contains the word "resolved" (e.g. "…operating within specifications", "…viable workaround"). This is the marker-less heuristic limitation called out in the approved spec — not a parsing bug (verified against raw entries). Consequence: a Slack feed monitor's page status reads "Active Incidents" even when nothing is ongoing, and those stale incidents seed as active.

I deliberately did not expand keyword-sniffing (mid-incident text like "we've identified a fix and are monitoring" would false-positive into resolved).

Recommended follow-up: Slack populates the Atom <summary> with a past-tense retrospective only on closed incidents — both correctly-resolved entries had it, the false-actives' resolution lived only in <content>. Treating "entry has a finalized <summary> retrospective" (or a generic staleness window) as a resolved signal would clear the false-actives. Left out of this PR as a behavior decision for review.

Docs

AGENTS.md, README.md, and docs/wiki/API-Integration.md updated. Design spec and implementation plan included under docs/superpowers/.

🤖 Generated with Claude Code

anthonybaldwin and others added 6 commits June 5, 2026 13:40
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Parse Atom and RSS feeds into the canonical Summary/Incident model:
detect feed kind, parse timestamped update blocks into stable-id updates
(marker- and prose-based status), synthesize page status, and expose a
Provider whose probe matches only real feed documents.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add "feed" to ProviderId and the monitor schema, register it last in
PROBE_ORDER (matches only real feed documents), and reword the /monitor add
failure to point users at a direct Atom/RSS feed URL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant