Skip to content

feat(status-pages): page service, publisher, break-glass import, auto incidents - #1929

Merged
Makisuo merged 15 commits into
status-pages/2-edgefrom
status-pages/3-backend
Oct 31, 2026
Merged

Makisuo merged 15 commits into
status-pages/2-edgefrom
status-pages/3-backend

fix(status-pages): publish restores lost routes, recovers from lost a…

4b26f13
Select commit
Loading
Failed to load commit list.
Maple Review Bot / Maple / review succeeded Oct 31, 2026 in 1m 10s

Confidence 8/10 · No issues found

🟢 Confidence 8/10 · likely safe to merge. Held at 8 because earlier reviews of this pull request found 7 defects.
3 notes · tests partial −1 · risk medium · 4/4 new units observable · 7 defects fixed in earlier rounds

Adds the status-page service, publisher, break-glass reconciler and auto incidents. The latest commit closes the publish-race, slug-route, defect-swallowing and mid-publish-delete gaps in the publisher and alert tick, each with a test that fails on the old code.

  • publish clears publishedHash and bumps the page when its compare-and-set loses
  • A page deleted mid-publish has its KV/R2 snapshot and route removed via cleanEdge
  • An unchanged publish re-reads the slug route and rewrites it when it is gone
  • syncStatusPages uses Effect.catchCause under a 3 s timeout, so a defect cannot abort the alert tick

Still open from earlier reviews

Fixed since the last review

  • ✅ F13 · A lost publish race keeps the winner's hash with a stale KV body, so no sweep rewrites it
  • ✅ F14 · A defect in syncStatusPages escapes Effect.catch and aborts the alert tick
  • ✅ F15 · putRoutes runs only inside if (written), so a lost or dropped slug route is never rewritten for an unchanged page

Production impact

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/status-pages/status-page-test-support.ts 1.7k GET 985/day, 1.4% err
GET 701/day, 11.2% err
packages/backend/src/services/status-pages/StatusPagePublisher.ts 1.1k StatusPagePublisher.sweep 526/day, 0.1% err
StatusPagePublisher.heartbeat 525/day, 0.0% err
StatusPagePublisher.publish 81/day, 0.0% err
packages/backend/src/services/status-pages/StatusPageReconciler.ts 1.1k StatusPageReconciler.reconcileAll 526/day, 0.0% err
StatusPageReconciler.repairAutoIncidents 526/day, 0.1% err
packages/backend/src/services/status-pages/StatusPageStore.ts 1.1k StatusPageStore.call 1.1k/day, 0.0% err
packages/backend/src/services/status-pages/StatusPageSnapshotBuilder.ts 81 StatusPageSnapshotBuilder.build 81/day, 0.0% err
packages/backend/src/services/status-pages/StatusPagesService.ts 26 StatusPagesService.get 16/day, 8.3% err
StatusPagesService.listComponents 10/day, 11.4% err
Telemetry this change adds and removes (18)
  • ➕ span name StatusPageReconciler.reconcilePage · packages/backend/src/services/status-pages/StatusPageReconciler.ts:243
  • ➕ span name StatusPageReconciler.reconcile · packages/backend/src/services/status-pages/StatusPageReconciler.ts:336
  • ➕ span name StatusPagesService.list · packages/backend/src/services/status-pages/StatusPagesService.ts:540
  • ➕ span name StatusPagesService.create · packages/backend/src/services/status-pages/StatusPagesService.ts:558
  • ➕ span name StatusPagesService.update · packages/backend/src/services/status-pages/StatusPagesService.ts:609
  • ➕ span name StatusPagesService.remove · packages/backend/src/services/status-pages/StatusPagesService.ts:667
  • ➕ span name StatusPagesService.rotateBreakGlassToken · packages/backend/src/services/status-pages/StatusPagesService.ts:689
  • ➕ span name StatusPagesService.revokeBreakGlassToken · packages/backend/src/services/status-pages/StatusPagesService.ts:718
  • ➕ span name StatusPagesService.createComponent · packages/backend/src/services/status-pages/StatusPagesService.ts:762
  • ➕ span name StatusPagesService.updateComponent · packages/backend/src/services/status-pages/StatusPagesService.ts:815
  • ➕ span name StatusPagesService.removeComponent · packages/backend/src/services/status-pages/StatusPagesService.ts:866
  • ➕ span name StatusPagesService.reorderComponents · packages/backend/src/services/status-pages/StatusPagesService.ts:888
  • ➕ span name StatusPagesService.listIncidents · packages/backend/src/services/status-pages/StatusPagesService.ts:931
  • ➕ span name StatusPagesService.getIncident · packages/backend/src/services/status-pages/StatusPagesService.ts:957
  • ➕ span name StatusPagesService.createIncident · packages/backend/src/services/status-pages/StatusPagesService.ts:965
  • ➕ span name StatusPagesService.postUpdate · packages/backend/src/services/status-pages/StatusPagesService.ts:1037
  • ➕ span name StatusPagesService.updateIncident · packages/backend/src/services/status-pages/StatusPagesService.ts:1090
  • ➕ span name StatusPagesService.removeIncident · packages/backend/src/services/status-pages/StatusPagesService.ts:1146
What was checked
  • Lost-race branch sets publishedHash: null and bumpPage, so the next sweep rewrites the body (StatusPagePublisher.ts:156,170)
  • catchCause catches defects, not just typed failures (AlertsService.ts:679), and summarizeCause is imported
  • Slug-route repair compares the KV page id before rewriting, so another page's slug is untouched (StatusPagePublisher.ts:121-126)
Observability coverage: 4 of 4 changes observable
Change Kind Observable Evidence
StatusPagePublisher.publish worker step yes StatusPagePublisher.publish span with maple.status_page.id, revision, written (StatusPagePublisher.ts:86,172)
StatusPagePublisher.sweep cron sweep yes StatusPagePublisher.sweep span with due/written/failed/cleaned counts and per-page failure logs (StatusPagePublisher.ts:263,308,336)
StatusPagePublisher.heartbeat cron side-write yes own span plus a warning log on store failure (StatusPagePublisher.ts:243,331)
Auto incident open/resolve from the alert tick background write in alert evaluation yes runs inside the traced alert tick; timeout/failure logged with orgId and maple.alert.incident_id (AlertsService.ts:680-687)

4b26f13 · Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.