feat(cron): warn when gateway not running on cron create/list (#51696)

The cron ticker only runs inside the gateway (_start_cron_ticker); there
is no standalone cron daemon. When the gateway isn't running, next_run_at
passes but jobs never fire and last_run_at stays null — and manual
'hermes cron run' (which bypasses the ticker) appears to work, masking
the real cause. This is the most common cron support report (#51038).

cron list already warned; extend the same warning to cron create (the
moment the user is most likely to hit this) via a shared helper, and add
a pointer to 'hermes cron status'. Silent when a gateway is running, so
the gateway /cron path is unaffected.
This commit is contained in:
Teknium 2026-06-23 23:29:50 -07:00 committed by GitHub
parent c39b2b50ee
commit 78e122ae1a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 83 additions and 6 deletions

View file

@ -57,6 +57,30 @@ def _cron_api(**kwargs):
return json.loads(cronjob_tool(**kwargs))
def _warn_if_gateway_not_running() -> None:
"""Warn that scheduled jobs won't fire unless the gateway is running.
The cron ticker only runs inside the gateway (``_start_cron_ticker`` in
gateway/run.py); there is no standalone cron daemon. Without a running
gateway, ``next_run_at`` passes but jobs never fire and ``last_run_at``
stays null the most common cron support report (#51038). Surfacing this
at create/list time, when the user is right there, prevents it.
"""
try:
from hermes_cli.gateway import find_gateway_pids
if find_gateway_pids():
return
except Exception:
# If we can't determine gateway state, stay quiet rather than nag.
return
print(color(" ⚠ Gateway is not running — jobs won't fire automatically.", Colors.YELLOW))
print(color(" Start it with: hermes gateway install", Colors.DIM))
print(color(" sudo hermes gateway install --system # Linux servers", Colors.DIM))
print(color(" Check status: hermes cron status", Colors.DIM))
def cron_list(show_all: bool = False):
"""List all scheduled jobs."""
from cron.jobs import list_jobs
@ -137,12 +161,7 @@ def cron_list(show_all: bool = False):
print()
from hermes_cli.gateway import find_gateway_pids
if not find_gateway_pids():
print(color(" ⚠ Gateway is not running — jobs won't fire automatically.", Colors.YELLOW))
print(color(" Start it with: hermes gateway install", Colors.DIM))
print(color(" sudo hermes gateway install --system # Linux servers", Colors.DIM))
print()
_warn_if_gateway_not_running()
def cron_tick():
@ -276,6 +295,7 @@ def cron_create(args):
if job_data.get("workdir"):
print(f" Workdir: {job_data['workdir']}")
print(f" Next run: {result['next_run_at']}")
_warn_if_gateway_not_running()
return 0

View file

@ -121,3 +121,60 @@ class TestCronCommandLifecycle:
out = capsys.readouterr().out
assert "Repeat: ∞" in out
class TestGatewayNotRunningWarning:
"""`cron create` / `cron list` must warn when the gateway (and thus the
cron ticker) isn't running, since jobs only fire inside the gateway.
Regression guard for #51038 — the most common cron 'jobs never fired'
report was simply a gateway that was never started.
"""
def test_create_warns_when_gateway_absent(self, tmp_cron_dir, capsys, monkeypatch):
monkeypatch.setattr("hermes_cli.gateway.find_gateway_pids", lambda: [])
cron_command(
Namespace(
cron_command="create",
schedule="0 11 * * *",
prompt="Daily report",
name="Daily 1130",
deliver=None,
repeat=None,
skill=None,
skills=None,
script=None,
workdir=None,
no_agent=False,
)
)
out = capsys.readouterr().out
assert "Created job" in out
assert "Gateway is not running" in out
def test_create_silent_when_gateway_running(self, tmp_cron_dir, capsys, monkeypatch):
monkeypatch.setattr("hermes_cli.gateway.find_gateway_pids", lambda: [4242])
cron_command(
Namespace(
cron_command="create",
schedule="0 11 * * *",
prompt="Daily report",
name="Daily 1130",
deliver=None,
repeat=None,
skill=None,
skills=None,
script=None,
workdir=None,
no_agent=False,
)
)
out = capsys.readouterr().out
assert "Created job" in out
assert "Gateway is not running" not in out
def test_list_warns_when_gateway_absent(self, tmp_cron_dir, capsys, monkeypatch):
create_job(prompt="Daily report", schedule="0 11 * * *")
monkeypatch.setattr("hermes_cli.gateway.find_gateway_pids", lambda: [])
cron_command(Namespace(cron_command="list", all=True))
out = capsys.readouterr().out
assert "Gateway is not running" in out