Skip to content

feat(alerts): add scheduleStartAt + scheduleOffsetMinutes#1745

Merged
kodiakhq[bot] merged 43 commits intohyperdxio:mainfrom
mlsalcedo:startTime-offset
Mar 6, 2026
Merged

feat(alerts): add scheduleStartAt + scheduleOffsetMinutes#1745
kodiakhq[bot] merged 43 commits intohyperdxio:mainfrom
mlsalcedo:startTime-offset

Conversation

@mlsalcedo
Copy link
Contributor

@mlsalcedo mlsalcedo commented Feb 17, 2026

Closes #1715

Summary

  • Add scheduleStartAt and scheduleOffsetMinutes to alert schemas and API validation.
  • Update alert evaluation scheduling to anchor windows by scheduleStartAt when set.
  • Skip evaluations before scheduleStartAt.
  • Keep current behavior unchanged when scheduling fields are unset.
  • Add UI fields and API/OpenAPI/external API support for the new schedule options.
  • Add alert scheduler tests for anchored windows and pre-start skip behavior.

Notes

  • This enables Splunk-style scheduled monitor migration where checks must run on isolated, periodic windows anchored to specific times.
  • scheduleStartAt is the primary anchor; scheduleOffsetMinutes remains optional for backward-compatible alignment.

@vercel
Copy link

vercel bot commented Feb 17, 2026

@melsalcedo is attempting to deploy a commit to the HyperDX Team on Vercel.

A member of the Team first needs to authorize it.

@changeset-bot
Copy link

changeset-bot bot commented Feb 17, 2026

🦋 Changeset detected

Latest commit: 05b8f25

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@hyperdx/api Minor
@hyperdx/app Minor
@hyperdx/common-utils Minor
@hyperdx/otel-collector Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

@sgarfinkel sgarfinkel left a comment

Choose a reason for hiding this comment

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

Left some feedback

mlsalcedo and others added 4 commits February 17, 2026 10:36
Co-authored-by: Sam Garfinkel <10210461+sgarfinkel@users.noreply.github.com>
Co-authored-by: Sam Garfinkel <10210461+sgarfinkel@users.noreply.github.com>
@teeohhem teeohhem self-requested a review February 19, 2026 18:09
@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

PR Review: feat(alerts): add scheduleStartAt + scheduleOffsetMinutes

No critical issues found. Implementation is well-structured with solid test coverage.

A few items worth noting:

  • WARNING: validateAlertScheduleOffsetMinutes skips offset validation when scheduleStartAt is set — The check scheduleOffsetMinutes >= intervalMinutes still runs even when scheduleStartAt is provided (though it correctly does nothing at that point since offset is forced to 0). Not a bug, just a minor clarity issue.

  • WARNING: Date range limits use approximate year lengths (365 * 24 * 60 * 60 * 1000) — won't account for leap years. Negligible in practice but worth noting.

  • INFO: Redundant max(1439) on base Zod schema in zod.ts and types.ts — the superRefine already enforces the dynamic per-interval max. Not harmful, just redundant.

  • INFO: makeAlert calls new Date(alert.scheduleStartAt) without error handling — safe because alertSchema.parse() is now called before makeAlert in all routers, ensuring scheduleStartAt is a valid ISO string. The recent change to explicitly parse in routers (previously req.body was used directly) is a good fix.

The backward-compat strategy via normalizeNoOpAlertScheduleFields and the null-vs-undefined distinction for scheduleStartAt serialization are well thought out.

Reviewed by Claude Sonnet 4.6

melsalcedo and others added 4 commits February 27, 2026 19:13
…arttime-offset

# Conflicts:
#	packages/api/src/utils/zod.ts
#	packages/app/src/DBSearchPageAlertModal.tsx
#	packages/app/src/components/DBEditTimeChartForm.tsx
Wrap scheduling settings (start offset and anchor start time) in a
collapsible section that auto-expands when the user has configured
non-default values. Adds inline description and tooltip help text
for each field.

Made-with: Cursor
Copy link
Contributor

@teeohhem teeohhem left a comment

Choose a reason for hiding this comment

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

things I added

  1. Advanced Settings collapsable menu for these new fields, along with text descriptions and tooltips.
  2. Date format in the timepicker to match the rest of the application.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 3, 2026

Claude finished @mlsalcedo's task in 2m 59s —— View job


Code Review

  • Gather context and read changed files
  • Analyze for critical issues
  • Post review

Review

Previous critical issues from the last review have been resolved:

  • Schema chain fixedalertSchema in zod.ts now correctly chains .and(zSavedSearchAlert.or(zTileAlert)).superRefine(validateAlertScheduleOffsetMinutes). The savedSearchId/dashboardId/tileId discriminator validation is no longer a dead no-op.
  • (alert as any) casts removed from DBEditTimeChartForm.tsx
  • Silent offset-0 drop fixednormalizeNoOpAlertScheduleFields now uses dirtyFields.alert?.scheduleOffsetMinutes as preserveExplicitScheduleOffsetMinutes consistently in both DBSearchPageAlertModal and DBEditTimeChartForm

Remaining items:

  • ⚠️ Double-parse overhead (packages/api/src/routers/api/alerts.ts:103, packages/api/src/routers/external-api/v2/alerts.ts:419, 511) — alertSchema.parse(req.body) is called again inside the handler after validateRequest middleware already ran it. The comment explains the rationale (enforce refinements, strip unknown fields). This is safe but adds redundant work on every request; consider a shared parsed-body approach if this becomes a perf concern.

  • ℹ️ name / message omitted from internal GET /api/alerts list (packages/api/src/routers/api/alerts.ts:67–79) — the _.pick doesn't include name or message. Pre-existing omission, not introduced by this PR.

  • ℹ️ AlertScheduleFields accordion opened state (packages/app/src/components/AlertScheduleFields.tsx:58) — useState(hasAdvancedScheduleValues) captures the initial value only. If a parent re-renders with a different scheduleOffsetMinutes/scheduleStartAt, the accordion won't auto-open. Acceptable for current UX since initial form values are set correctly on mount.

✅ No blocking issues. Implementation is solid — backward-compat strategy, validation chain, and scheduler window logic are all correct.

@mlsalcedo
Copy link
Contributor Author

  • Advanced Settings collapsable menu for these new fields, along with text descriptions and tooltips.
  • Date format in the timepicker to match the rest of the application.

Done

@github-actions
Copy link
Contributor

github-actions bot commented Mar 3, 2026

Claude finished @mlsalcedo's task in 1m 37s —— View job


Code Review

  • Gather context and read changed files
  • Analyze for critical issues
  • Post review

Review

Previous critical issues have been resolved. The implementation looks solid — schema chain, type safety, and backward-compat strategy are all correct.

A few remaining items:

  • ⚠️ scheduleStartAt: null stored explicitly in MongoDB (packages/api/src/models/alert.ts) — When a user clears scheduleStartAt, null is persisted to the document (vs. the key being absent on pre-migration alerts). checkAlerts handles this correctly via scheduleStartAt == null, but any future queries filtering { scheduleStartAt: { $exists: false } } would miss these documents. Low risk now, worth noting for migrations.

  • ⚠️ getScheduledWindowStart exported but has no test for scheduleOffsetMinutes > 0 with anchored start (packages/api/src/tasks/checkAlerts/index.ts:113) — The function logs that offset is ignored when scheduleStartAt is set, but there's no unit test confirming scheduleOffsetMinutes is actually ignored in the return value. The processAlert path normalizes this, but getScheduledWindowStart itself doesn't enforce it.

  • ℹ️ processRequest vs validateRequest switch (packages/api/src/routers/api/alerts.ts, external-api/v2/alerts.ts) — Good change. processRequest replaces req.body with the parsed+stripped result, enforcing refinements (including superRefine) where validateRequest did not. The behavior change is intentional and correct.

  • ℹ️ useState(hasAdvancedScheduleValues) initial capture (packages/app/src/components/AlertScheduleFields.tsx:58) — Pre-existing from previous review: accordion won't auto-open if parent re-renders with new schedule values post-mount. Acceptable for current UX.

✅ No blocking issues. Ready to merge pending reviewer approval.

…arttime-offset

# Conflicts:
#	packages/api/src/routers/api/__tests__/alerts.test.ts
#	packages/api/src/routers/api/alerts.ts
#	packages/api/src/routers/external-api/v2/alerts.ts
@github-actions
Copy link
Contributor

github-actions bot commented Mar 3, 2026

Claude finished @mlsalcedo's task in 1m 37s —— View job


Code Review

  • Gather context and read changed files
  • Analyze for critical issues
  • Post review

Review

All critical issues from previous reviews have been resolved. The implementation is solid — schema chain, type safety, backward-compat strategy, and scheduler window logic are correct.

Remaining items (carry-overs from prior reviews):

  • ⚠️ getScheduledWindowStart accepts scheduleOffsetMinutes even when scheduleStartAt is set (packages/api/src/tasks/checkAlerts/index.ts:249–271) — the function itself doesn't drop the offset; only processAlert enforces the ignore-behavior. If called directly with both params, it silently applies offset anyway. No test confirms offset is ignored at the return-value level. Low risk since callers normalize, but a comment or assertion inside the function would help.

  • ℹ️ scheduleStartAt: null written explicitly to MongoDB on clear (packages/api/src/models/alert.ts) — future queries using { $exists: false } would miss these docs. Pre-migration alerts use key-absent, post-clear alerts use null. Consider coercing to key-absent on read/write if this becomes a migration concern.

  • ℹ️ useState(hasAdvancedScheduleValues) captures initial value only (packages/app/src/components/AlertScheduleFields.tsx:58) — accordion won't auto-open if parent passes new schedule values post-mount. Acceptable for the current UX since initial form values are set correctly.

✅ No blocking issues. Ready to merge pending reviewer approval.

@teeohhem
Copy link
Contributor

teeohhem commented Mar 4, 2026

@claude please review and fix the conflicts

@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

Claude Code is working…

I'll analyze this and get back to you.

View job run

…arttime-offset

# Conflicts:
#	packages/api/src/tasks/checkAlerts/__tests__/checkAlerts.test.ts
#	packages/api/src/tasks/checkAlerts/index.ts
@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

Claude finished @mlsalcedo's task in 1m 36s —— View job


Code Review

  • Check for merge conflicts
  • Review code for critical issues

Conflict Status

The conflicts previously flagged by GitHub were already resolved in merge commit d1c329a5 ("Merge remote-tracking branch 'upstream/main' into codex/issue-1715-starttime-offset"). The branch is fully up-to-date with origin/main — no pending conflicts remain.

The conflict resolution in checkAlerts/index.ts and checkAlerts/__tests__/checkAlerts.test.ts correctly integrates both the new scheduling logic (getScheduledWindowStart, alertHasGroupBy, scheduleStartAt pre-skip) and the upstream whereLanguage fixes for tile alerts.

Review

✅ No critical issues. The implementation is solid and previous review blockers have all been addressed.

One carry-over worth noting before merge:

  • ⚠️ getScheduledWindowStart doesn't enforce offset=0 when scheduleStartAt is set (packages/api/src/tasks/checkAlerts/index.ts:150–168) — the function accepts both params but only processAlert enforces the ignore-behavior via a log warning. If called directly with both, offset is silently applied. A guard or comment inside the function would prevent future misuse. Fix this →

@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

Claude finished @mlsalcedo's task in 37s —— View job


Code Review

  • Read changed files
  • Analyze for critical issues
  • Post review

Review

All previously flagged critical issues have been resolved. The implementation looks correct.

One minor carry-over worth noting:

  • ⚠️ getScheduledWindowStart silently accepts scheduleOffsetMinutes when scheduleStartAt is set (packages/api/src/tasks/checkAlerts/index.ts:148–169) — if called directly with both params, offset is applied anyway; only processAlert enforces ignore-behavior. A guard or comment inside the function would prevent future misuse. Fix this →

✅ No blocking issues. Ready to merge pending reviewer approval.

…arttime-offset

# Conflicts:
#	packages/app/src/components/DBEditTimeChartForm.tsx
#	packages/common-utils/src/types.ts
@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

@teeohhem
Copy link
Contributor

teeohhem commented Mar 6, 2026

@mlsalcedo thanks for your contribution and sticking with all the conflicts/changes.

@kodiakhq kodiakhq bot merged commit 902b8eb into hyperdxio:main Mar 6, 2026
14 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Alert Scheduling with Absolute Start Time (plus optional offset)

4 participants