feat(status): surface relevant Cloudflare provider incidents - #1296
Conversation
Fetch Cloudflare's public Statuspage unresolved incidents on the status worker cron tick, filter to products kody runs on, cache fail-soft, and render a separate provider section plus outage-email annotations. Refs #1092
Root vitest console spies fail unexpected console.warn; the fail-soft Statuspage paths intentionally warn, so the test opts in and asserts.
Complete the opt-in that was partially staged by lint-staged so root vitest console spies do not fail the intentional warn coverage.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 38ca4bf. Configure here.
|
🔎 Preview deployed: https://kody-pr-1296.kody-a99.workers.dev Worker: Mocks:
|
|
Warning Review limit reached
Next review available in: 1 minute You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe status package now fetches and caches relevant Cloudflare incidents. Cached incidents appear on status pages and in outage-related emails. Fetch failures are handled without changing outage detection or notifications. ChangesCloudflare incident integration
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant StatusStore
participant CloudflareStatuspage
participant StatusSnapshot
participant StatusPage
participant EmailPolicy
StatusStore->>CloudflareStatuspage: fetch unresolved provider incidents
CloudflareStatuspage-->>StatusStore: return relevant incident data
StatusStore->>StatusSnapshot: store cached provider incidents
StatusSnapshot->>StatusPage: provide incidents for rendering
StatusSnapshot->>EmailPolicy: provide incidents for alert composition
StatusPage-->>StatusSnapshot: render provider incident section
EmailPolicy-->>StatusSnapshot: append annotations to applicable emails
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Require updatedAt and affectedComponents in parseProviderIncidentCache so a corrupt meta entry cannot throw during status page HTML rendering.
|
@coderabbitai review |
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
packages/status/status-page.node.test.ts (1)
126-132: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAssert the remaining provider incident metadata.
The fixture includes
affectedComponentsandupdatedAt, but the test does not verify them. Add assertions so the renderer cannot omit these fields without failing the test.Proposed assertions
expect(withProvider).toContain('https://stspg.io/r2') +expect(withProvider).toContain('affects R2') +expect(withProvider).toContain('updated 2026-08-07T19:00:00.000Z') expect(withProvider).toContain('for context only')🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/status/status-page.node.test.ts` around lines 126 - 132, Extend the withProvider assertions in the status-page rendering test to verify the fixture’s affectedComponents and updatedAt values are present in the rendered output. Use the exact expected metadata from the fixture and preserve all existing assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/status/provider-incidents.ts`:
- Around line 179-181: Update the cache validation around the fetchedAt age
check to reject future-dated records as well as records older than maxAgeMs.
Ensure cache entries are accepted only when fetchedAt is no later than now and
the computed age is within the existing freshness window, while preserving the
incident-array validation and filtering flow.
- Around line 124-127: Update shortlink handling in renderProviderIncident to
validate the trimmed URL before assigning it to href. Allow only HTTPS URLs
whose host matches the expected Cloudflare Statuspage hosts, and use
cloudflareStatusPageUrl for missing, malformed, non-HTTPS, or other-host values.
In `@packages/status/status-page.ts`:
- Line 216: Validate and normalize incident.shortlink in renderProviderIncident
before inserting it into the href: use cloudflareStatusPageUrl when missing, and
replace links whose protocol or host is not an approved Cloudflare Statuspage
URL. Keep escaping the final validated URL with escapeHtml before rendering.
- Line 223: Add a defensive fallback for snapshot.providerIncidents before the
length check in renderStatusPage, treating missing legacy values as null so
accessing length cannot throw; preserve the existing empty-string behavior for
null or empty incident lists.
---
Nitpick comments:
In `@packages/status/status-page.node.test.ts`:
- Around line 126-132: Extend the withProvider assertions in the status-page
rendering test to verify the fixture’s affectedComponents and updatedAt values
are present in the rendered output. Use the exact expected metadata from the
fixture and preserve all existing assertions.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 5b8c8df9-ae42-496a-b4c8-8d388cda85a3
📒 Files selected for processing (9)
packages/status/email-policy.node.test.tspackages/status/email-policy.tspackages/status/provider-incidents.node.test.tspackages/status/provider-incidents.tspackages/status/readme.mdpackages/status/status-page.node.test.tspackages/status/status-page.tspackages/status/status-store.tspackages/status/status-types.ts
Sanitize shortlinks to Cloudflare Statuspage HTTPS hosts, reject future-dated cache entries, and treat missing providerIncidents as absent.

Related to #1092 (alerting independence).
Summary
During the 2026-08-07 audit-DB probe timeout, Cloudflare had an active R2 availability incident — but the status page couldn't show that context, leaving "is this us or the platform?" unanswerable at a glance.
This PR adds provider-incident context to the status worker (entirely within
packages/status/):/api/v2/incidents/unresolved.json), keeping only incidents whose components intersect what kody runs on (Workers, D1, R2, KV, Durable Objects, Queues, Vectorize, Email Routing, Access); regional/product noise excluded.StatusStore; the render path reads the cache only.{ ok: false }); a stale-but-present cache keeps rendering; the page renders without the section when nothing is available.Conductor report
Status: in review. Track agent (Grok 4.5) completed the implementation; PR creation was blocked by the Cursor manual-approval gate, so the conductor created this PR from the pushed branch. Track agent resumes the ship-pr deploy loop: CI + AI reviewers → squash-merge → verify the path-filtered
status:deployjob and the live page at status.heykody.dev.Summary by CodeRabbit