/** * Monitor sweep scheduling. * * The sweep rides the event bus's existing `timer.tick.5m` rather than * introducing a scheduler: the bus's timer menu is deliberately fixed * (packages/event-bus/src/timer.ts), and a five-minute grid is accurate enough * for home-lab monitoring cadences. Consequence, accepted: a monitor's real * period is its configured interval rounded up to the next sweep. * * See openspec/changes/add-alerting/design.md D2. */ const MINUTE_MS = 60_000; export interface SchedulableMonitor { id: string; intervalMinutes: number; enabled: boolean; /** Null when the monitor has never run. */ lastRunAt: Date | null; } /** * Select the monitors due to run at `now`. * * A monitor that has never run is due immediately — otherwise a newly created * monitor would stay silent for a full interval, which reads as "monitoring is * broken" precisely when someone has just switched it on and is watching. * * Disabled monitors are never due. Their alerts are resolved when they are * disabled rather than left to hang, so skipping them here cannot strand a * firing alert. */ export function selectDueMonitors(monitors: T[], now: Date): T[] { return monitors.filter((monitor) => { if (!monitor.enabled) return false; if (monitor.lastRunAt === null) return true; const elapsed = now.getTime() - monitor.lastRunAt.getTime(); return elapsed >= monitor.intervalMinutes * MINUTE_MS; }); }