hermes-agent/tests/cron
Ben b01eee0c77 feat(cron): store-level CAS claim for multi-machine at-most-once fire
Phase 4C. claim_job_for_fire(job_id, *, claim_ttl_seconds=300) in cron/jobs.py:
under the existing _jobs_lock() file lock, claim a job for a single external
fire so that across N gateway replicas exactly ONE wins. Single-machine
deployments always win (unaffected).

Semantics:
- missing / disabled / paused job → False.
- a fresh fire_claim (younger than claim_ttl_seconds) already present → False
  (someone else holds it). Stale claim (crashed winner) → overwrite, so a job
  is never wedged forever.
- on win: stamp fire_claim={at, by:_machine_id()}; for recurring (cron/interval)
  advance next_run_at (mirrors advance_next_run's at-most-once bump so a stale
  re-delivery can't re-fire); one-shots keep next_run_at but the fresh claim
  blocks a duplicate retry for the same fire.
- mark_job_run now clears fire_claim on completion so a re-armed recurring job
  is claimable again next fire.

_machine_id() (HERMES_MACHINE_ID env, else hostname:pid) is attribution-only;
correctness is the file lock + fresh-claim check, not the id.

This is consumed by CronScheduler.fire_due (Phase 4B). tick is untouched — it
still uses advance_next_run, so the built-in single-machine path is unaffected.

Tests (real store, temp HERMES_HOME): claim-once-then-block + next_run advance,
one-shot no-double-claim, unknown→False, paused→False, stale-claim reclaimable,
mark_job_run clears the claim (recurring re-claimable). tests/cron/ 470 passed.
2026-06-18 14:34:34 +10:00
..
__init__.py test: add unit tests for 8 modules (batch 2) 2026-02-26 13:54:20 +03:00
test_blueprint_catalog.py docs: finish Automation Blueprints terminology rebrand (#44470) 2026-06-11 17:22:22 -04:00
test_claim_job_for_fire.py feat(cron): store-level CAS claim for multi-machine at-most-once fire 2026-06-18 14:34:34 +10:00
test_codex_execution_paths.py refactor(session-log): delete _save_session_log and all callers 2026-05-20 11:44:10 -07:00
test_compute_next_run_last_run_at.py fix(cron): use last_run_at as croniter base for cron jobs 2026-04-29 08:24:48 -07:00
test_cron_context_from.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_cron_inactivity_timeout.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_cron_no_agent.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_cron_prompt_injection_skill.py fix(cron): don't strict-scan script-injected output in no-skills jobs (#43223) 2026-06-10 08:27:24 +05:30
test_cron_script.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_cron_workdir.py fix(cron): make sequential jobs non-blocking too + sweep MCP after jobs finish 2026-06-04 05:40:13 -07:00
test_cronjob_schema.py test(cron): guard schedule-required description text on CRONJOB_SCHEMA 2026-05-26 14:09:37 -07:00
test_file_permissions.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_jobs.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_jobs_crossprocess_lock.py fix: complete cron jobs lock salvage 2026-06-15 06:29:00 -07:00
test_parallel_pool.py revert(cron): remove per-job profile support (PR #28124) (#43956) 2026-06-10 20:46:17 -07:00
test_rewrite_skill_refs.py fix(curator): rewrite cron job skill refs after consolidation (#18253) 2026-04-30 23:04:50 -07:00
test_run_one_job.py refactor(cron): extract run_one_job shared firing helper from tick 2026-06-18 14:26:29 +10:00
test_scheduler.py Merge remote-tracking branch 'origin/main' into hermes/hermes-6b48295e 2026-06-11 07:38:25 -07:00
test_scheduler_mcp_init.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_scheduler_provider.py feat(cron): additive CronScheduler hooks (on_jobs_changed/fire_due/reconcile) 2026-06-18 14:30:31 +10:00
test_suggestions.py refactor(cron): rebrand Cron Recipes -> Automation Blueprints 2026-06-11 10:49:47 -07:00