# NousResearch/hermes-agent · Cron tasks and SOUL.md identity handling without workdir

## Open now

- Nothing open on GitHub.

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.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #17509. Verify it addresses your case.
- **Recommended:** read PR #17509 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-17509`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #16756: \[Bug\]: CRON tasks do not apply prompts defined in SOUL.md

## Prior & competing PRs

- **#17509: fix(cron): keep SOUL.md identity when workdir is unset (#16756)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `cron/scheduler.py`, `run_agent.py`, `scripts/release.py`, `tests/cron/test_cron_workdir.py`, `tests/run_agent/test_run_agent.py`
  - fix hunks:
  - `cron/scheduler.py`

    ```diff
    @@ -1033,10 +1033,12 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
                 enabled_toolsets=_resolve_cron_enabled_toolsets(job, _cfg),
                 disabled_toolsets=["cronjob", "messaging", "clarify"],
                 quiet_mode=True,
    -            # When a workdir is configured, inject AGENTS.md / CLAUDE.md /
    -            # .cursorrules from that directory; otherwise preserve the old
    -            # behaviour (don't inject SOUL.md/AGENTS.md from the scheduler cwd).
    +            # Cron jobs should always inherit the user's SOUL.md identity from
    +            # HERMES_HOME. When a workdir is configured, also inject project
    +            # context files (AGENTS.md / CLAUDE.md / .cursorrules) from there.
    +            # Without a workdir, keep cwd context discovery disabled.
                 skip_context_files=not bool(_job_workdir),
    +            load_soul_identity=True,
                 skip_memory=True,  # Cron system prompts would corrupt user representations
                 platform="cron",
                 session_id=_cron_session_id,
    ```
  - `run_agent.py`

    ```diff
    @@ -926,6 +926,7 @@ def __init__(
             thread_id: str = None,
             gateway_session_key: str = None,
             skip_context_files: bool = False,
    +        load_soul_identity: bool = False,
             skip_memory: bool = False,
             session_db=None,
             parent_session_id: str = None,
    ```

    ```diff
    @@ -977,6 +978,9 @@ def __init__(
                 skip_context_files (bool): If True, skip auto-injection of SOUL.md, AGENTS.md, and .cursorrules
                     into the system prompt. Use this for batch processing and data generation to avoid
                     polluting trajectories with user-specific persona or project instructions.
    +            load_soul_identity (bool): If True, still use ~/.hermes/SOUL.md as the primary
    +                identity even when skip_context_files=True. Project context files from the cwd
    +                remain skipped.
             """
             _install_safe_stdio()
     
    ```

    ```diff
    @@ -1005,6 +1009,7 @@ def __init__(
             self._print_fn = None
             self.background_review_callback = None  # Optional sync callback for gateway delivery
             self.skip_context_files = skip_context_files
    +        self.load_soul_identity = load_soul_identity
             self.pass_session_id = pass_session_id
             self._credential_pool = [redacted:secret-assignment]
             self.log_prefix_chars = log_prefix_chars
    ```

    ```diff
    @@ -4742,9 +4747,11 @@ def _build_system_prompt(self, system_message: str = None) -> str:
             #   6. Current date & time (frozen at build time)
             #   7. Platform-specific formatting hint
     
    -        # Try SOUL.md as primary identity (unless context files are skipped)
    +        # Try SOUL.md as primary identity unless the caller explicitly skipped it.
    +        # Some execution modes (cron) still want HERMES_HOME persona while keeping
    +        # cwd project instructions disabled.
             _soul_loaded = False
    -        if not self.skip_context_files:
    +        if self.load_soul_identity or not self.skip_context_files:
                 _soul_content = load_soul_md()
                 if _soul_content:
                     prompt_parts = [_soul_content]
    ```
  - `scripts/release.py`

    ```diff
    @@ -67,6 +67,7 @@
         "[redacted:email]": "BeliefanX",
         "[redacted:email]": "Mind-Dragon",
         "[redacted:email]": "westers",
    +    "[redacted:email]": "liuhao1024",
         "130918800+devorun@users.noreply.github.com": "devorun",
         "[redacted:email]": "beesrsj2500",
         "[redacted:email]": "beesrsj2500",
    ```
  - `tests/cron/test_cron_workdir.py`

    ```diff
    @@ -265,6 +265,7 @@ def _install_stubs(monkeypatch, observed: dict):
             class FakeAgent:
                 def __init__(self, **kwargs):
                     observed["skip_context_files"] = kwargs.get("skip_context_files")
    +                observed["load_soul_identity"] = kwargs.get("load_soul_identity")
                     observed["terminal_cwd_during_init"] = os.environ.get(
                         "TERMINAL_CWD", "_UNSET_"
                     )
    ```

    ```diff
    @@ -335,6 +336,7 @@ def test_workdir_sets_and_restores_terminal_cwd(
     
             # AIAgent was built with skip_context_files=False (feature ON).
             assert observed["skip_context_files"] is False
    +        assert observed["load_soul_identity"] is True
             # TERMINAL_CWD was pointing at the job workdir while the agent ran.
             assert observed["terminal_cwd_during_init"] == str(tmp_path.resolve())
             assert observed["terminal_cwd_during_run"] == str(tmp_path.resolve())
    ```

    ```diff
    @@ -373,6 +375,8 @@ def test_no_workdir_leaves_terminal_cwd_untouched(self, monkeypatch):
     
             # Feature is OFF — skip_context_files stays True.
             assert observed["skip_context_files"] is True
    +        # Cron still forces SOUL.md identity even when cwd context files stay off.
    +        assert observed["load_soul_identity"] is True
             # TERMINAL_CWD saw the same value during init as it had before.
             assert observed["terminal_cwd_during_init"] == before
             # And after run_job completes, it's still the sentinel (nothing
    ```
  - `tests/run_agent/test_run_agent.py`

    ```diff
    @@ -862,6 +862,26 @@ def test_always_has_identity(self, agent):
             prompt = agent._build_system_prompt()
             assert DEFAULT_AGENT_IDENTITY in prompt
     
    +    def test_can_use_soul_identity_even_when_context_files_are_skipped(self):
    +        with (
    +            patch("run_agent.get_tool_definitions", return_value=_make_tool_defs("terminal")),
    +            patch("run_agent.check_toolset_requirements", return_value={}),
    +            patch("run_agent.OpenAI"),
    +            patch("run_agent.load_soul_md", return_value="SOUL IDENTITY"),
    +        ):
    +            agent = AIAgent(
    +                api_key="[redacted:secret-assignment]",
    +                base_url="https://openrouter.ai/api/v1",
    +                quiet_mode=True,
    +                skip_context_files=True,
    +                load_soul_identity=True,
    +                skip_memory=True,
    +            )
    +            prompt = agent._build_system_prompt()
    +
    +        assert "SOUL IDENTITY" in prompt
    +        assert DEFAULT_AGENT_IDENTITY not in prompt
    +
         def test_includes_system_message(self, agent):
             prompt = agent._build_system_prompt(system_message="Custom instruction")
             assert "Custom instruction" in prompt
    ```
- **#16947: fix(cron): keep SOUL.md identity without workdir**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#17509`, `#16756`

## Fix status

- Merged to main in PR #17509 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-04-29`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
