Files
hermes-agent/cron/occurrences.py
T
Teknium 7d6d0709cd fix(cron): skip completed scheduled occurrences after snapshot rollback
Capture the exact UTC scheduled instant before either due scanning or the
external fire claim advances jobs.json. Bind it to the durable attempt
before worker handoff; manual and unclassified direct attempts stay null.
Consult any retained completed matching row, independently of stale stamps,
claim-time windows, and newer failed attempts. Preserve unknown and legacy
attempt eligibility rather than guessing that a side effect completed.

Real isolated restart probes reproduce duplicate script writes on base and
suppress them on the fix for builtin tick and provider fire. Distinct and
manual occurrences still execute. The campaign-serialized cron suite is
queued; this progressive commit preserves the verified integration step.

Credit holny's issue #104790 and guard proposal #104323; exact identity
replaces the approximation rather than importing its legacy heuristic.

Co-authored-by: holny <holny@foxmail.com>
2026-09-07 05:57:26 -07:00

37 lines
1.2 KiB
Python

"""Exact scheduled identities, independent of mutable jobs.json dispatch stamps."""
from datetime import datetime, timezone
import logging
logger = logging.getLogger(__name__)
def scheduled_instant(value):
"""Canonicalize aware instants; legacy/ambiguous values carry no exact identity."""
if not isinstance(value, str):
return None
try:
instant = datetime.fromisoformat(value)
if instant.tzinfo is None:
return None
return instant.astimezone(timezone.utc).isoformat()
except ValueError:
return None
def completed_occurrence(job, instant):
"""Unknown/failed/pruned attempts cannot prove completion: keep them eligible."""
from cron.executions import _transaction
instant = scheduled_instant(instant)
if instant is None:
return False
try:
with _transaction() as conn:
return conn.execute(
"SELECT 1 FROM executions WHERE job_id=? AND scheduled_instant=? "
"AND status='completed' LIMIT 1", (str(job['id']), instant)
).fetchone() is not None
except Exception:
logger.warning("Cannot check completed occurrence for job %s", job['id'], exc_info=True)
return False