fix(cron): action='run' executes inline when no gateway ticker is running

Closes #16612.

cronjob(action='run') and POST /api/jobs/{id}/run previously only called
trigger_job(), which just sets next_run_at=now. When no gateway process
was running, there was no ticker to pick the job up, so last_run_at
stayed null forever — the tool returned success=true with nothing
actually executing.

- cron.scheduler: extract the per-job run+save+deliver+mark pipeline
  from tick()'s closure into a module-level _execute_and_record helper,
  and add run_job_now() which acquires the shared tick file-lock and
  runs one job inline (advancing next_run_at first for recurring jobs
  to preserve at-most-once semantics).
- tools/cronjob_tools: branch action='run' on gateway presence via
  find_gateway_pids(). Ticker up → defer to next tick (≤60s) with a
  clear message. Ticker down → execute inline via run_job_now() and
  return the updated job including last_run_at/last_status. Tool
  schema description updated to reflect the two modes.
- gateway/platforms/api_server: same branching on the HTTP endpoint,
  running the inline call off the event loop via run_in_executor.
- hermes_cli/cron: CLI 'hermes cron run' now surfaces the message,
  last_run_at, and last_status from the tool result instead of
  always printing 'It will run on the next scheduler tick.'
- docs: update cli-commands and cron-troubleshooting to describe the
  inline-when-no-gateway behaviour.

Tests: 11 new unit tests (tests/cron/test_run_job_now.py) covering
run_job_now's inline path, _execute_and_record's success/empty/error
paths, and the tool/API gateway-presence branching. Plus a new
TestRunJob::test_run_job_executes_inline_when_no_gateway on the
api_server suite. E2E verified in a temp HERMES_HOME against the
real file-based job store.
This commit is contained in:
teknium1
2026-04-29 08:18:11 -07:00
parent 810d98e892
commit 9f361cdca6
8 changed files with 643 additions and 58 deletions

View File

@@ -295,7 +295,7 @@ hermes cron <list|create|edit|pause|resume|run|remove|status|tick>
| `edit` | Update a job's schedule, prompt, name, delivery, repeat count, or attached skills. Supports `--clear-skills`, `--add-skill`, and `--remove-skill`. |
| `pause` | Pause a job without deleting it. |
| `resume` | Resume a paused job and compute its next future run. |
| `run` | Trigger a job on the next scheduler tick. |
| `run` | Run a job now. Executes inline when no gateway is running; otherwise defers to the next gateway tick (≤60s). |
| `remove` | Delete a scheduled job. |
| `status` | Check whether the cron scheduler is running. |
| `tick` | Run due jobs once and exit. |