{"repo":"NousResearch/hermes-agent","public_id":"1589","display_label":"Remove unnecessary crontab requirement from cronjob tool","summary":"A bug was reported that crontab is required even when crons are internal, followed by multiple fix attempts removing that unnecessary dependency from the cronjob tool.","subsystem":"cron","counts":{"members":4,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":2},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":1638,"merged_at":"2026-03-17T08:40:02+00:00"},"resolution":{"state":"resolved","by":"pr-1638"},"freshness":{"state":"stale","last_verified_at":"2026-03-17T08:40:02+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.5},"fix_edges":[{"issue_number":1589,"pr_number":1638,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-03-17T08:40:02+00:00"}],"members":[{"kind":"issue","number":1589,"title":"[Bug]: Crontab is required despite crons being internal?","url":null,"state":"closed"},{"kind":"pr","number":1610,"title":"fix(tools): remove unnecessary crontab dependency from cronjob tools","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#1638","number":1638},{"kind":"pr","value":"#1633","number":1633}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":1633,"title":"fix(tools): remove unnecessary crontab requirement from cronjob tool","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#1638","number":1638}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":1638,"title":"fix(tools): remove unnecessary crontab requirement from cronjob tool","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["tests/tools/test_cronjob_tools.py","tools/cronjob_tools.py"],"fix_hunks":[{"file":"tests/tools/test_cronjob_tools.py","hunks":["@@ -62,22 +62,44 @@ def test_deception_blocked(self):\n \n \n class TestCronjobRequirements:\n-    def test_requires_crontab_binary_even_in_interactive_mode(self, monkeypatch):\n+    def test_requires_no_crontab_binary(self, monkeypatch):\n+        \"\"\"Cron is internal (JSON-based scheduler), no system crontab needed.\"\"\"\n         monkeypatch.setenv(\"HERMES_INTERACTIVE\", \"1\")\n         monkeypatch.delenv(\"HERMES_GATEWAY_SESSION\", raising=False)\n         monkeypatch.delenv(\"HERMES_EXEC_ASK\", raising=False)\n-        monkeypatch.setattr(\"shutil.which\", lambda name: None)\n-\n-        assert check_cronjob_requirements() is False\n+        # Even with no crontab in PATH, the cronjob tool should be available\n+        # because hermes uses an internal scheduler, not system crontab.\n+        assert check_cronjob_requirements() is True\n \n-    def test_accepts_interactive_mode_when_crontab_exists(self, monkeypatch):\n+    def test_accepts_interactive_mode(self, monkeypatch):\n         monkeypatch.setenv(\"HERMES_INTERACTIVE\", \"1\")\n         monkeypatch.delenv(\"HERMES_GATEWAY_SESSION\", raising=False)\n         monkeypatch.delenv(\"HERMES_EXEC_ASK\", raising=False)\n-        monkeypatch.setattr(\"shutil.which\", lambda name: \"/usr/bin/crontab\")\n \n         assert check_cronjob_requirements() is True\n \n+    def test_accepts_gateway_session(self, monkeypatch):\n+        monkeypatch.delenv(\"HERMES_INTERACTIVE\", raising=False)\n+        monkeypatch.setenv(\"HERMES_GATEWAY_SESSION\", \"1\")\n+        monkeypatch.delenv(\"HERMES_EXEC_ASK\", raising=False)\n+\n+        assert check_cronjob_requirements() is True\n+\n+    def test_accepts_exec_ask(self, monkeypatch):\n+        monkeypatch.delenv(\"HERMES_INTERACTIVE\", raising=False)\n+        monkeypatch.delenv(\"HERMES_GATEWAY_SESSION\", raising=False)\n+        monkeypatch.setenv(\"HERMES_EXEC_ASK\", \"1\")\n+\n+        assert check_cronjob_requirements() is True\n+\n+    def test_rejects_when_no_session_env(self, monkeypatch):\n+        \"\"\"Without any session env vars, cronjob tool should not be available.\"\"\"\n+        monkeypatch.delenv(\"HERMES_INTERACTIVE\", raising=False)\n+        monkeypatch.delenv(\"HERMES_GATEWAY_SESSION\", raising=False)\n+        monkeypatch.delenv(\"HERMES_EXEC_ASK\", raising=False)\n+\n+        assert check_cronjob_requirements() is False\n+\n \n # =========================================================================\n # schedule_cronjob"]},{"file":"tools/cronjob_tools.py","hunks":["@@ -8,7 +8,6 @@\n import json\n import os\n import re\n-import shutil\n import sys\n from pathlib import Path\n from typing import Any, Dict, List, Optional","@@ -414,13 +413,10 @@ def check_cronjob_requirements() -> bool:\n     \"\"\"\n     Check if cronjob tools can be used.\n \n-    Requires 'crontab' executable to be present in the system PATH.\n     Available in interactive CLI mode and gateway/messaging platforms.\n+    The cron system is internal (JSON file-based scheduler ticked by the gateway),\n+    so no external crontab executable is required.\n     \"\"\"\n-    # Ensure the system can actually install and manage cron entries.\n-    if not shutil.which(\"crontab\"):\n-        return False\n-\n     return bool(\n         os.getenv(\"HERMES_INTERACTIVE\")\n         or os.getenv(\"HERMES_GATEWAY_SESSION\")\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}