{"repo":"NousResearch/hermes-agent","public_id":"16756","display_label":"Cron tasks and SOUL.md identity handling without workdir","summary":"These members appear to concern cron tasks not applying SOUL.md identity or prompts, particularly when no workdir is set. Two are titled as fixes and one as a bug report; their titles alone do not show whether the problem was fully addressed.","subsystem":null,"counts":{"members":3,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":1},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":17509,"merged_at":"2026-04-29T15:10:25+00:00"},"resolution":{"state":"resolved","by":"pr-17509"},"freshness":{"state":"stale","last_verified_at":"2026-04-29T15:10:25+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.6666666666666666},"fix_edges":[{"issue_number":16756,"pr_number":17509,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-04-29T15:10:25+00:00"}],"members":[{"kind":"issue","number":16756,"title":"[Bug]: CRON tasks do not apply prompts defined in SOUL.md","url":null,"state":"closed"},{"kind":"pr","number":16947,"title":"fix(cron): keep SOUL.md identity without workdir","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#17509","number":17509},{"kind":"pr","value":"#16756","number":16756}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":17509,"title":"fix(cron): keep SOUL.md identity when workdir is unset (#16756)","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["cron/scheduler.py","run_agent.py","scripts/release.py","tests/cron/test_cron_workdir.py","tests/run_agent/test_run_agent.py"],"fix_hunks":[{"file":"cron/scheduler.py","hunks":["@@ -1033,10 +1033,12 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:\n             enabled_toolsets=_resolve_cron_enabled_toolsets(job, _cfg),\n             disabled_toolsets=[\"cronjob\", \"messaging\", \"clarify\"],\n             quiet_mode=True,\n-            # When a workdir is configured, inject AGENTS.md / CLAUDE.md /\n-            # .cursorrules from that directory; otherwise preserve the old\n-            # behaviour (don't inject SOUL.md/AGENTS.md from the scheduler cwd).\n+            # Cron jobs should always inherit the user's SOUL.md identity from\n+            # HERMES_HOME. When a workdir is configured, also inject project\n+            # context files (AGENTS.md / CLAUDE.md / .cursorrules) from there.\n+            # Without a workdir, keep cwd context discovery disabled.\n             skip_context_files=not bool(_job_workdir),\n+            load_soul_identity=True,\n             skip_memory=True,  # Cron system prompts would corrupt user representations\n             platform=\"cron\",\n             session_id=_cron_session_id,"]},{"file":"run_agent.py","hunks":["@@ -926,6 +926,7 @@ def __init__(\n         thread_id: str = None,\n         gateway_session_key: str = None,\n         skip_context_files: bool = False,\n+        load_soul_identity: bool = False,\n         skip_memory: bool = False,\n         session_db=None,\n         parent_session_id: str = None,","@@ -977,6 +978,9 @@ def __init__(\n             skip_context_files (bool): If True, skip auto-injection of SOUL.md, AGENTS.md, and .cursorrules\n                 into the system prompt. Use this for batch processing and data generation to avoid\n                 polluting trajectories with user-specific persona or project instructions.\n+            load_soul_identity (bool): If True, still use ~/.hermes/SOUL.md as the primary\n+                identity even when skip_context_files=True. Project context files from the cwd\n+                remain skipped.\n         \"\"\"\n         _install_safe_stdio()\n ","@@ -1005,6 +1009,7 @@ def __init__(\n         self._print_fn = None\n         self.background_review_callback = None  # Optional sync callback for gateway delivery\n         self.skip_context_files = skip_context_files\n+        self.load_soul_identity = load_soul_identity\n         self.pass_session_id = pass_session_id\n         self._credential_pool = [redacted:secret-assignment]\n         self.log_prefix_chars = log_prefix_chars","@@ -4742,9 +4747,11 @@ def _build_system_prompt(self, system_message: str = None) -> str:\n         #   6. Current date & time (frozen at build time)\n         #   7. Platform-specific formatting hint\n \n-        # Try SOUL.md as primary identity (unless context files are skipped)\n+        # Try SOUL.md as primary identity unless the caller explicitly skipped it.\n+        # Some execution modes (cron) still want HERMES_HOME persona while keeping\n+        # cwd project instructions disabled.\n         _soul_loaded = False\n-        if not self.skip_context_files:\n+        if self.load_soul_identity or not self.skip_context_files:\n             _soul_content = load_soul_md()\n             if _soul_content:\n                 prompt_parts = [_soul_content]"]},{"file":"scripts/release.py","hunks":["@@ -67,6 +67,7 @@\n     \"[redacted:email]\": \"BeliefanX\",\n     \"[redacted:email]\": \"Mind-Dragon\",\n     \"[redacted:email]\": \"westers\",\n+    \"[redacted:email]\": \"liuhao1024\",\n     \"130918800+devorun@users.noreply.github.com\": \"devorun\",\n     \"[redacted:email]\": \"beesrsj2500\",\n     \"[redacted:email]\": \"beesrsj2500\","]},{"file":"tests/cron/test_cron_workdir.py","hunks":["@@ -265,6 +265,7 @@ def _install_stubs(monkeypatch, observed: dict):\n         class FakeAgent:\n             def __init__(self, **kwargs):\n                 observed[\"skip_context_files\"] = kwargs.get(\"skip_context_files\")\n+                observed[\"load_soul_identity\"] = kwargs.get(\"load_soul_identity\")\n                 observed[\"terminal_cwd_during_init\"] = os.environ.get(\n                     \"TERMINAL_CWD\", \"_UNSET_\"\n                 )","@@ -335,6 +336,7 @@ def test_workdir_sets_and_restores_terminal_cwd(\n \n         # AIAgent was built with skip_context_files=False (feature ON).\n         assert observed[\"skip_context_files\"] is False\n+        assert observed[\"load_soul_identity\"] is True\n         # TERMINAL_CWD was pointing at the job workdir while the agent ran.\n         assert observed[\"terminal_cwd_during_init\"] == str(tmp_path.resolve())\n         assert observed[\"terminal_cwd_during_run\"] == str(tmp_path.resolve())","@@ -373,6 +375,8 @@ def test_no_workdir_leaves_terminal_cwd_untouched(self, monkeypatch):\n \n         # Feature is OFF — skip_context_files stays True.\n         assert observed[\"skip_context_files\"] is True\n+        # Cron still forces SOUL.md identity even when cwd context files stay off.\n+        assert observed[\"load_soul_identity\"] is True\n         # TERMINAL_CWD saw the same value during init as it had before.\n         assert observed[\"terminal_cwd_during_init\"] == before\n         # And after run_job completes, it's still the sentinel (nothing"]},{"file":"tests/run_agent/test_run_agent.py","hunks":["@@ -862,6 +862,26 @@ def test_always_has_identity(self, agent):\n         prompt = agent._build_system_prompt()\n         assert DEFAULT_AGENT_IDENTITY in prompt\n \n+    def test_can_use_soul_identity_even_when_context_files_are_skipped(self):\n+        with (\n+            patch(\"run_agent.get_tool_definitions\", return_value=_make_tool_defs(\"terminal\")),\n+            patch(\"run_agent.check_toolset_requirements\", return_value={}),\n+            patch(\"run_agent.OpenAI\"),\n+            patch(\"run_agent.load_soul_md\", return_value=\"SOUL IDENTITY\"),\n+        ):\n+            agent = AIAgent(\n+                api_key=\"[redacted:secret-assignment]\",\n+                base_url=\"https://openrouter.ai/api/v1\",\n+                quiet_mode=True,\n+                skip_context_files=True,\n+                load_soul_identity=True,\n+                skip_memory=True,\n+            )\n+            prompt = agent._build_system_prompt()\n+\n+        assert \"SOUL IDENTITY\" in prompt\n+        assert DEFAULT_AGENT_IDENTITY not in prompt\n+\n     def test_includes_system_message(self, agent):\n         prompt = agent._build_system_prompt(system_message=\"Custom instruction\")\n         assert \"Custom instruction\" in prompt\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}