/** * Alert rule catalog. Each entry is a pure async function that takes a * `portId` and returns an array of `AlertCandidate` rows the engine should * upsert into `alerts`. Skeleton: signatures only — implementations land * in PR2. */ import type { AlertCandidate } from './alerts.service'; import { ALERT_RULES, type AlertRuleId } from '@/lib/db/schema/insights'; type RuleEvaluator = (portId: string) => Promise; /** Empty implementations — every evaluator returns no candidates. PR2 * fills these in; the cron dispatcher in PR2 walks `RULE_REGISTRY`. */ export const RULE_REGISTRY: Record = { 'reservation.no_agreement': async () => [], 'interest.stale': async () => [], 'document.expiring_soon': async () => [], 'document.signer_overdue': async () => [], 'berth.under_offer_stalled': async () => [], 'expense.duplicate': async () => [], 'expense.unscanned': async () => [], 'interest.high_value_silent': async () => [], 'eoi.unsigned_long': async () => [], 'audit.suspicious_login': async () => [], }; /** Sanity check: catalog matches the ALERT_RULES literal type. */ export function listRuleIds(): readonly AlertRuleId[] { return ALERT_RULES; }