hermes-agent/tests/cron
Teknium ed903f953e feat(cron): pre-dispatch configuration validation (blocked_config + alert-once)
Validate a job's configuration BEFORE any agent machinery is constructed:

- missing provider API key (AuthError from a read-only
  resolve_runtime_provider probe; skipped when a fallback_providers chain
  is configured, since auth-fallback may rescue the run)
- attached skill not ready (skill_view readiness_status=setup_needed —
  missing required env vars / commands / credential files)
- delivery platform unknown or unconnected (deliver=local/origin/all are
  never checked; gateway-config load failures fail open)

On a failing check run_job returns a [blocked_config]-marked error without
constructing AIAgent/MCP/etc, so a misconfigured job never burns an LLM
call. run_one_job records last_status='blocked_config' and delivers the
alert exactly ONCE across ticks (persisted preflight_alerted bit — the
alert-once shape from the #73506 dead-pin auto-pause); the next healthy
run clears the marker so a future break re-alerts. Every preflight check
fails open: only an affirmative misconfiguration verdict blocks.

Config: cron.preflight (default true); `cron.preflight: false` restores
the old fail-during-run behavior. Documented in the cron user guide and
config defaults.

mark_job_run gains an optional status= override (unblocked call shape
unchanged) and drops preflight_alerted on any successful run.

Tests: tests/cron/test_preflight_config.py (blocked_config + no agent +
single alert across two ticks, healthy job unaffected, recovery clears
dedup, fallback-chain rescue, opt-out restores old behavior, skill
readiness miss, unknown delivery platform, deliver=local never loads
gateway config). Full tests/cron/ + cronjob tool suite green (525 tests).

Ported from: paperclipai/paperclip execution-semantics §5 (MIT);
in-repo precedent: #27948, #73506
2026-08-07 08:57:53 -07:00
..
__init__.py
…
conftest.py refactor(cron): polish registration partial-failure surfaces 2026-08-07 17:45:06 +05:30
test_blueprint_catalog.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_claim_job_for_fire.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_codex_execution_paths.py test(cron): accept target_model kwarg in codex-path resolver stub 2026-07-19 09:57:21 -07:00
test_compute_next_run_last_run_at.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_cron_context_from.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_cron_direct_api_call_62151.py fix: join heartbeat thread in finally + add error-path test 2026-08-05 14:00:25 +05:30
test_cron_direct_api_call_watchdog.py fix(cron): move watchdog state under the request lock; fail closed on resolver errors 2026-08-07 15:32:37 +05:30
test_cron_inactivity_timeout.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_cron_kanban_env_isolation.py fix(cron): thread per-run prompt through cronjob(action='run') (#57331) 2026-08-06 23:14:55 -07:00
test_cron_no_agent.py fix: close simplify-pass findings — scheduler sibling site + home-unresolvable totality 2026-08-06 17:36:40 +05:30
test_cron_profile_isolation.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_cron_prompt_injection_skill.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_cron_provider_pin.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_cron_script.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_cron_workdir.py fix(cron): thread per-run prompt through cronjob(action='run') (#57331) 2026-08-06 23:14:55 -07:00
test_cronjob_schema.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_execution_ledger.py fix(cron): thread per-run prompt through cronjob(action='run') (#57331) 2026-08-06 23:14:55 -07:00
test_file_permissions.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_idle_tick_config_skip.py perf(cron): skip config load on idle scheduler ticks (idea from #33612) 2026-08-03 17:18:51 +05:30
test_jobs.py fix(cron): retain completed one-shot jobs instead of deleting them on completion 2026-08-02 23:12:00 -07:00
test_jobs_changed_notify.py refactor(cron): polish registration partial-failure surfaces 2026-08-07 17:45:06 +05:30
test_jobs_crossprocess_lock.py fix: complete cron jobs lock salvage 2026-06-15 06:29:00 -07:00
test_jobs_file_ownership.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_jobs_shrink_merge_80624.py perf(cron): stat-stamp fast path for the shrink-merge; no caller-list mutation 2026-08-07 17:29:49 +05:30
test_monitor_kind.py feat(cron): monitor-mode jobs — hash-suppressed change detection 2026-08-07 08:57:44 -07:00
test_notepad.py feat(cron): per-job durable notepad — KV scratchpad surviving scheduled runs 2026-08-07 08:57:48 -07:00
test_parallel_pool.py fix(cron): thread per-run prompt through cronjob(action='run') (#57331) 2026-08-06 23:14:55 -07:00
test_preflight_config.py feat(cron): pre-dispatch configuration validation (blocked_config + alert-once) 2026-08-07 08:57:53 -07:00
test_reasoning_config_per_model.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_rewrite_skill_refs.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_run_one_job.py feat(cron): pre-dispatch configuration validation (blocked_config + alert-once) 2026-08-07 08:57:53 -07:00
test_scheduler.py fix(cron): thread per-run prompt through cronjob(action='run') (#57331) 2026-08-06 23:14:55 -07:00
test_scheduler_cron_session_isolation.py fix(cron): scope cron approval context per session 2026-08-03 00:25:20 +05:30
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 test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_scheduler_shutdown_guard.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_script_claim_heartbeat.py fix: update heartbeat test stubs to accept workdir kwarg 2026-07-24 15:55:39 -07:00
test_sessiondb_init_hang.py perf(cron): batch advance_next_run for the due-dispatch loop 2026-08-02 21:15:16 +05:30
test_shutdown_interrupt.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
test_suggestions.py fix(cron): surface initial scheduler registration failures 2026-08-07 17:45:06 +05:30
test_terminal_cwd_lock.py test(cron): pin fail-closed TERMINAL_CWD lock timeout behavior 2026-08-07 18:11:41 +05:30
test_ticker_stall_60703.py test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00