9d0bef369f
AsyncIOScheduler was constructed with no job_defaults, so APScheduler's default misfire_grace_time of 1s applied. In this single-process app the scheduler shares one event loop with the API and all other jobs, so when a daily job came due while the loop was busy (e.g. the scanner mid-run), the fire was processed >1s late, flagged a misfire, and skipped — while next_run still advanced 24h, making the job look healthy though it never ran. Set a generous grace window (1h), coalesce missed runs into a single catch-up, and cap concurrency at 1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>