You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Cron triggers double-firing on every slot — cron-service.schedule now accepts an optional ScheduleOptions.initialLastFired and seeds job.lastFired from it (defaults to null, preserving back-compat). trigger-service.startCronJob passes { initialLastFired: trigger.lastFiredAt ?? null } so the same-minute guard trips on the re-arm path: a fire → fireTrigger → updateTrigger({lastFiredAt}) → stopCronJob + startCronJob cycle previously produced a fresh job with lastFired: null that the next interval poll re-fired in the same minute. Reading from the persisted record means a pm2/server restart inside the same minute as the last fire is also blocked from re-firing.
Technical Details
New cron-service.test.ts (5 specs) covers initialLastFired defaults, the seed value being honored, and fake-timer regression tests verifying the seeded job does NOT fire on the same-minute poll but DOES fire once the wall clock crosses into the next minute.
New trigger-service.test.ts (2 specs) verifies createTrigger schedules with initialLastFired: null and that a fireTrigger → re-arm cycle passes lastFiredAt as the seed.
Validated in production at the 10:00 CDMX slot — caty-archived-scan and caty-deadline-check confirmed firing exactly once after a pm2 restart (tsx watch alone doesn't clear stale setIntervals).