Skip to content

feat(alerts): alert when a rule's window has no data - #1208

Merged
Makisuo merged 2 commits into
feat/mcp-preview-alert-rulefrom
feat/alert-no-data-behavior-alert
Oct 2, 2026
Merged

Makisuo merged 2 commits into
feat/mcp-preview-alert-rulefrom
feat/alert-no-data-behavior-alert

Conversation

@Makisuo

@Makisuo Makisuo commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Stack 4/5. An empty window was always skipped (except throughput </<=, which reads it as zero), so a rule whose query stopped matching went silent.

  • Rules take an opt-in alertOnNoData (alert_on_no_data on v2 and the MCP create/update tools, a switch under Evaluation timing in the app; default off). It compiles to noDataBehavior: "alert": an empty window evaluates as a breach with no value and opens/resolves an incident through the usual counters.

  • It wins over the low-throughput zero read, which a minimum sample count would otherwise skip.

  • Grouped rules reject it (the app disables the switch): an empty result has no real group to breach under, and a group that stops reporting is already held by its incident's telemetry check. On a raw SQL rule with a group column it fires only when the query returns no rows at all, and the preview models exactly that.

  • Changing the behavior resolves open incidents. Notifications read "has no data over the last 5m" where the value would be.

  • no_data_behavior was already a stored compiled column, so no DB migration. v2 rules expose alert_on_no_data (so the IaC AlertRule resource can declare it without drift) and it is in the audit diff.

  • Alert rule docs cover the samples column, the switch, and skip reasons.

Heads-up: no_data_behavior gains the enum value alert. iOS builds generated from the previous spec will fail to decode a rule using it. The app reads rules through try?, so it degrades to missing rule names, and the spec and fixture are regenerated here.

Test: alerting-core, AlertsService.test.ts (empty warehouse opens an incident; throughput precedence; grouped rejection; grouped raw preview), summary line, v2 contract, alert-tools, web alerts libs and collection, alchemy contract.

Summary by CodeRabbit

  • New Features
    • Alert rules can now treat empty data windows as breaches and trigger incidents. This option is available for ungrouped rules; grouped rules do not support it.
    • Raw SQL alert rules can use a samples column to report sample counts. Without one, each row counts as one sample.
  • Improvements
    • Alert details and notifications now explain when a window has no data and show the relevant threshold.
    • Troubleshooting guidance better distinguishes skipped checks due to missing data from checks below the minimum sample count.

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 6db8a891-8bc7-49f7-8bcd-f912702d6782

📥 Commits

Reviewing files that changed from the base of the PR and between 4259130 and 2ecfd03.

📒 Files selected for processing (28)
  • apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts
  • apps/ai/src/mcp/tools/create-alert-rule.ts
  • apps/ai/src/mcp/tools/get-alert-rule.ts
  • apps/ai/src/mcp/tools/preview-alert-rule.ts
  • apps/ai/src/mcp/tools/update-alert-rule.ts
  • apps/api/src/routes/v2/alert-rules.http.ts
  • apps/ios/Maple/Fixtures/FixtureAPI.swift
  • apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json
  • apps/landing/src/content/docs/alerting/alert-rules.md
  • apps/web/src/components/alerts/signal-and-threshold-section.tsx
  • apps/web/src/lib/alerts/diagnosis.ts
  • apps/web/src/lib/alerts/form-utils.test.ts
  • apps/web/src/lib/alerts/form-utils.ts
  • apps/web/src/lib/collections/alerts.ts
  • packages/alchemy-maple/src/AlertRule.ts
  • packages/alerting-core/src/index.test.ts
  • packages/alerting-core/src/index.ts
  • packages/backend/src/services/alerts/AlertDeliveryDispatch.test.ts
  • packages/backend/src/services/alerts/AlertDeliveryDispatch.ts
  • packages/backend/src/services/alerts/AlertRuleModel.ts
  • packages/backend/src/services/alerts/AlertsService.test.ts
  • packages/backend/src/services/alerts/AlertsService.ts
  • packages/backend/src/services/alerts/alert-formatting.ts
  • packages/domain/src/http/alerts.ts
  • packages/domain/src/http/v2/alert-rules.ts
  • packages/domain/src/http/v2/v2-contract.test.ts
  • packages/domain/src/mcp-outputs/alerts.ts
  • packages/domain/src/query-engine.ts
 _______________________________________
< For a good time, call 1-800-COD-RABT. >
 ---------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@maple-review-bot

maple-review-bot Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 4/5 · likely safe to merge
The new branch is a small, well-tested addition to the evaluation core, and the update paths that could silently clear the flag were each re-read and set it explicitly.
quality 100/100 · no findings · tests covered · risk medium

Adds an opt-in alertOnNoData rule flag that compiles to the new noDataBehavior: "alert" and makes an empty window evaluate as a breach with a null value, wired through v2, the MCP tools, the app switch, IaC and the iOS spec. No defect found; safe to merge.

  • evaluateAlertObservation breaches on an empty window when noDataBehavior is alert
  • compileRulePlan maps alertOnNoData to alert after the throughput zero rule
  • v2 rule exposes alert_on_no_data; PATCH merge and MCP tools carry it
  • formatObservedSummary renders No data (threshold …) for a null value
What was checked
  • The new alert branch precedes the minimumSampleCount gate (alerting-core/src/index.ts:99), so a blind rule still fires while thin data stays skipped
  • patch.alert_on_no_data ?? doc.noDataBehavior === "alert" keeps an explicit false (apps/api/src/routes/v2/alert-rules.http.ts:311)
  • desiredBody forwards every declared prop, so alert_on_no_data reaches the v2 request (packages/alchemy-maple/src/AlertRule.ts:116)

2504a7a · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@Makisuo
Makisuo force-pushed the feat/alert-no-data-behavior-alert branch from 2504a7a to 2ecfd03 Compare October 2, 2026 17:48
@maple-review-bot

maple-review-bot Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Note

A newer push replaced 2ecfd03 before its review finished. The latest commit is reviewed in a new comment.

@Makisuo
Makisuo added this pull request to stack #1210 October 2, 2026 17:50
@Makisuo
Makisuo force-pushed the feat/alert-no-data-behavior-alert branch from 2ecfd03 to 042af39 Compare October 2, 2026 17:53
@maple-review-bot

maple-review-bot Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Note

A newer push replaced 042af39 before its review finished. The latest commit is reviewed in a new comment.

A window with no data was always skipped (bar low-throughput rules, which
read it as zero), so a rule whose query stopped matching, typically a raw
SQL rule, went quiet with nothing to say it was blind.

Rules take an opt-in alertOnNoData (alert_on_no_data on the v2 API and the
MCP create/update tools, a switch under Evaluation timing in the app). It
compiles to noDataBehavior "alert": an empty window evaluates as a breach
with no value, so it opens and resolves an incident through the usual
breach and healthy counts. Notifications read "No data" where the value
would be. The stored no_data_behavior column already held the compiled
value, so no migration is needed. get_alert_rule shows the behavior, the
v2 rule exposes alert_on_no_data, and the IaC AlertRule resource accepts it.

The alert rule docs now cover the samples column, the new switch, and how
skipped checks report why.
…rules

- alertOnNoData now wins over the low-throughput zero read. Read as zero, an
  empty window was skipped under a minimum sample count instead of breaching,
  and the flag read back as off on every rebuild (edits, the v2 rule, IaC
  drift).
- Grouped rules reject it: an empty result breached under the engine's "all"
  key, never a real group, and a group that stops reporting is already held
  by its incident's telemetry check. The app disables the switch on grouped
  rules.
- The preview treats a group's missing windows as skips, as the scheduler
  never evaluates them, and charts the empty-result series where nothing
  reported.
- Changing the no-data behavior resolves open incidents, the testRule
  fallback goes through applyEvaluationLogic, the summary line reads "has no
  data" in place of "n/a", alert_on_no_data is in the audit diff, and the
  Electric alert rows read an unknown behavior as skip.
@Makisuo
Makisuo force-pushed the feat/alert-no-data-behavior-alert branch from 042af39 to cd80cc0 Compare October 2, 2026 17:56
@maple-review-bot

maple-review-bot Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Maple review

🟡 Confidence 3/5 · needs attention
The scheduler/preview logic and grouped rejection check out and are well tested; the one unmitigated risk is the new alert value on the v2 wire, which older generated clients cannot decode.
quality 90/100 · 1 warning · tests covered · risk high · 1/1 new units observable

Adds an opt-in alert-on-no-data mode that treats an empty window as a breach, wired through the scheduler, preview, v2 API, MCP tools and the rule form, with grouped rules rejected. The evaluation logic and tests hold up; the new alert value on the v2 wire breaks older generated clients.

  • Rules gain alertOnNoData, compiled to noDataBehavior: "alert" (AlertRuleModel.ts:263)
  • An empty window breaches with a null value (alerting-core/src/index.ts:99)
  • Grouped plans reject the flag; changing it resolves open incidents
  • v2, MCP, alchemy and the app form expose alert_on_no_data

Findings

🟠 Warning · F1 · no_data_behavior: "alert" on the v2 wire breaks older generated clients

correctness · packages/domain/src/query-engine.ts:711

no_data_behavior is serialized from this literal, so the first rule saved with alert_on_no_data returns "alert" — a value the checked-in spec has no enum case for in clients generated before this PR. HomeModel.swift:201 and IncidentsListView.swift:31 decode the rule page with try? await rules.items, so one such rule fails the whole page and every incident card loses its rule name until the app is rebuilt from the new spec. Keep the wire value in {skip, zero} for this field (map alert to skip in toV2Rule at apps/api/src/routes/v2/alert-rules.http.ts:89) and let alert_on_no_data carry the new state, or expose the new mode behind a versioned field.

Decouple the wire contract from the stored mode: emit `skip`/`zero` in `no_data_behavior` and report alert-on-no-data through `alert_on_no_data` only, so shipped clients keep decoding rules.
🤖 Prompt to fix this finding with an AI agent
Findings from an automated review of commit cd80cc09b33f425d47cdffa69abbc5c0be068fa3. Verify each one against the current code before changing anything, fix only those that still apply, and keep each fix to the lines it names.

---

F1 · Warning · correctness · packages/domain/src/query-engine.ts:711
`no_data_behavior: "alert"` on the v2 wire breaks older generated clients
`no_data_behavior` is serialized from this literal, so the first rule saved with `alert_on_no_data` returns `"alert"` — a value the checked-in spec has no enum case for in clients generated before this PR. `HomeModel.swift:201` and `IncidentsListView.swift:31` decode the rule page with `try? await rules.items`, so one such rule fails the whole page and every incident card loses its rule name until the app is rebuilt from the new spec. Keep the wire value in `{skip, zero}` for this field (map `alert` to `skip` in `toV2Rule` at `apps/api/src/routes/v2/alert-rules.http.ts:89`) and let `alert_on_no_data` carry the new state, or expose the new mode behind a versioned field.
Suggested fix: Decouple the wire contract from the stored mode: emit `skip`/`zero` in `no_data_behavior` and report alert-on-no-data through `alert_on_no_data` only, so shipped clients keep decoding rules.
What was checked
  • Grouped rejection goes through planGroupingTokens, so a builder-query rule grouped by its draft is still rejected (AlertRuleModel.ts:798)
  • The breach that comes from an empty window never enters the hold path: planAlertLifecycle gates on status === "healthy" (alerting-core/src/index.ts:436)
  • The preview's synthetic empty-result series uses toStorageGroupKey(plan, "all"), the same storage key the scheduler opens under (AlertRuleModel.ts:193)
Observability coverage: 1 of 1 changes observable
Change Kind Observable Evidence
no-data breach in runSchedulerTick background yes runs inside the existing scheduler span and incident/delivery metrics; no new call, log or entrypoint added

cd80cc0 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 inline note from Maple's review. The score and summary are in the review comment above.

* What an empty window evaluates to: `skip` the check, read it as `zero`, or
* `alert`, which counts it as a breach so a rule that goes blind opens an incident.
*/
export const QueryEngineNoDataBehavior = Schema.Literals(["skip", "zero", "alert"]).annotate({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

no_data_behavior: "alert" on the v2 wire breaks older generated clients

F1 · Warning · correctness

no_data_behavior is serialized from this literal, so the first rule saved with alert_on_no_data returns "alert" — a value the checked-in spec has no enum case for in clients generated before this PR. HomeModel.swift:201 and IncidentsListView.swift:31 decode the rule page with try? await rules.items, so one such rule fails the whole page and every incident card loses its rule name until the app is rebuilt from the new spec. Keep the wire value in {skip, zero} for this field (map alert to skip in toV2Rule at apps/api/src/routes/v2/alert-rules.http.ts:89) and let alert_on_no_data carry the new state, or expose the new mode behind a versioned field.

Decouple the wire contract from the stored mode: emit `skip`/`zero` in `no_data_behavior` and report alert-on-no-data through `alert_on_no_data` only, so shipped clients keep decoding rules.
🤖 Prompt to fix with an AI agent
In `packages/domain/src/query-engine.ts:711`: `no_data_behavior: "alert"` on the v2 wire breaks older generated clients.

`no_data_behavior` is serialized from this literal, so the first rule saved with `alert_on_no_data` returns `"alert"` — a value the checked-in spec has no enum case for in clients generated before this PR. `HomeModel.swift:201` and `IncidentsListView.swift:31` decode the rule page with `try? await rules.items`, so one such rule fails the whole page and every incident card loses its rule name until the app is rebuilt from the new spec. Keep the wire value in `{skip, zero}` for this field (map `alert` to `skip` in `toV2Rule` at `apps/api/src/routes/v2/alert-rules.http.ts:89`) and let `alert_on_no_data` carry the new state, or expose the new mode behind a versioned field.

Suggested fix: Decouple the wire contract from the stored mode: emit `skip`/`zero` in `no_data_behavior` and report alert-on-no-data through `alert_on_no_data` only, so shipped clients keep decoding rules.

Verify the problem exists at that location before changing it, and keep the fix to those lines.

@Makisuo
Makisuo merged commit 902758b into main Oct 2, 2026
41 of 42 checks passed
@Makisuo
Makisuo deleted the feat/alert-no-data-behavior-alert branch October 2, 2026 18:00
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