#5428: fix(Cron): prevent one-shot loop on skip
docs
Cluster:
Cron Job Enhancements
Fixes #5397
Problem:
- One‑shot `at` cron jobs with past `nextRunAtMs` could stay due after a skipped run, causing rapid re-fire loops.
Solution:
- Treat one‑shot jobs as single-run even when skipped or errored, so they don’t remain due.
Changes:
- Stop re-scheduling one‑shot jobs after the first run attempt.
- Added a test for past-due one‑shot jobs to confirm no loop.
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR adjusts one-shot (`schedule.kind === "at"`) cron job scheduling so jobs only auto-run once, even if the first run attempt is skipped/errored. Specifically, `computeJobNextRunAtMs` now returns `undefined` after any first attempt (`state.lastRunAtMs`), preventing past-due one-shot jobs from remaining due and re-firing in a tight loop. A new Vitest regression test covers the past-due + skipped scenario.
The change fits into the cron service by affecting how `recomputeNextRuns` derives `job.state.nextRunAtMs`, which in turn drives `isJobDue` and the scheduler wake-up logic.
<h3>Confidence Score: 4/5</h3>
- This PR is likely safe to merge and addresses the reported one-shot looping behavior with a small, localized scheduling change plus a regression test.
- The core logic change is confined to `computeJobNextRunAtMs` for `at` schedules and aligns with the stated intent (treat one-shot as single-attempt). The added test increases confidence. Remaining risk is mainly around behavioral expectations for one-shot jobs that previously retried until success; if any callers relied on that retry semantics, this change is a behavior shift rather than a pure bugfix.
- src/cron/service/jobs.ts (one-shot scheduling semantics), src/cron/service.runs-one-shot-main-job-disables-it.test.ts (regression test robustness).
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
**Context used:**
- Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=fd949e91-5c3a-4ab5-90a1-cbe184fd6ce8))
- Context from `dashboard` - AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=0d0c8278-ef8e-4d6c-ab21-f5527e322f13))
<!-- /greptile_comment -->
Most Similar PRs
#11813: fix(cron): ensure 'at' schedule type correctly registers nextWakeAt...
by leo-reifying · 2026-02-08
87.9%
#12443: fix(cron): don't advance past-due jobs that haven't been executed
by rummangeminicode · 2026-02-09
86.9%
#12303: fix(cron): correct nextRunAtMs calculation and prevent timer stall
by colddonkey · 2026-02-09
85.6%
#11657: fix(cron): treat skipped heartbeat as ok for one-shot jobs
by DukeDeSouth · 2026-02-08
85.5%
#8379: fix(cron): handle past-due one-shot 'at' jobs that haven't run yet
by Gerrald12312 · 2026-02-04
85.4%
#3693: fix(cron): delete deleteAfterRun jobs regardless of execution status
by HirokiKobayashi-R · 2026-01-29
85.3%
#18960: fix: don't disable one-shot cron jobs on skipped status
by jwchmodx · 2026-02-17
85.1%
#8034: fix(cron): run past-due one-shot jobs immediately on startup
by FelixFoster · 2026-02-03
84.4%
#8811: fix(cron): handle atMs fallback for kind=at jobs
by hlibr · 2026-02-04
84.3%
#11108: fix(cron): prevent missed jobs from being skipped on timer recompute
by Bentlybro · 2026-02-07
84.2%