fix: normalize repeat<=0 to None to prevent cron jobs deleting after first run (#2612)

fix: normalize repeat<=0 to None — cron jobs deleted after first run when LLM passes -1
This commit is contained in:
Mibay
2026-03-23 14:35:43 +01:00
committed by GitHub
parent f60ebc7bf2
commit ca2958ff98
3 changed files with 26 additions and 2 deletions

View File

@@ -383,6 +383,10 @@ def create_job(
"""
parsed_schedule = parse_schedule(schedule)
# Normalize repeat: treat 0 or negative values as None (infinite)
if repeat is not None and repeat <= 0:
repeat = None
# Auto-set repeat=1 for one-shot schedules if not specified
if parsed_schedule["kind"] == "once" and repeat is None:
repeat = 1
@@ -571,7 +575,7 @@ def mark_job_run(job_id: str, success: bool, error: Optional[str] = None):
# Check if we've hit the repeat limit
times = job["repeat"].get("times")
completed = job["repeat"]["completed"]
if times is not None and completed >= times:
if times is not None and times > 0 and completed >= times:
# Remove the job (limit reached)
jobs.pop(i)
save_jobs(jobs)

View File

@@ -313,6 +313,24 @@ class TestMarkJobRun:
# Job should be removed after hitting repeat limit
assert get_job(job["id"]) is None
def test_repeat_negative_one_is_infinite(self, tmp_cron_dir):
# LLMs often pass repeat=-1 to mean "infinite/forever".
# The job must NOT be deleted after runs when repeat <= 0.
job = create_job(prompt="Forever", schedule="every 1h", repeat=-1)
# -1 should be normalised to None (infinite) at create time
assert job["repeat"]["times"] is None
# Running it multiple times should never delete it
for _ in range(3):
mark_job_run(job["id"], success=True)
assert get_job(job["id"]) is not None, "job was deleted after run despite infinite repeat"
def test_repeat_zero_is_infinite(self, tmp_cron_dir):
# repeat=0 should also be treated as None (infinite), not "run zero times".
job = create_job(prompt="ZeroRepeat", schedule="every 1h", repeat=0)
assert job["repeat"]["times"] is None
mark_job_run(job["id"], success=True)
assert get_job(job["id"]) is not None
def test_error_status(self, tmp_cron_dir):
job = create_job(prompt="Fail", schedule="every 1h")
mark_job_run(job["id"], success=False, error="timeout")

View File

@@ -266,8 +266,10 @@ def cronjob(
if base_url is not None:
updates["base_url"] = _normalize_optional_job_value(base_url, strip_trailing_slash=True)
if repeat is not None:
# Normalize: treat 0 or negative as None (infinite)
normalized_repeat = None if repeat <= 0 else repeat
repeat_state = dict(job.get("repeat") or {})
repeat_state["times"] = repeat
repeat_state["times"] = normalized_repeat
updates["repeat"] = repeat_state
if schedule is not None:
parsed_schedule = parse_schedule(schedule)