# NousResearch/hermes-agent · Kanban worker TERMINAL_CWD path resolution bug and fixes

## Open now

- Nothing open on GitHub.

A bug where Kanban workers resolve relative file paths against the wrong working directory (gateway home instead of the assigned workspace) and several related fix attempts targeting TERMINAL_CWD environment variable handling.

## TL;DR

- 4 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 2 PR(s) closed without merging.
- A fix was merged to main in PR #50348. Verify it addresses your case.
- **Recommended:** read PR #50348 (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: `unresolved`. 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.50`: 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

- #41312: \[Bug\]: Kanban worker resolves relative file paths against TERMINAL_CWD (gateway home), not the assigned HERMES_KANBAN_WORKSPACE

## Prior & competing PRs

- **#41450: fix(kanban): set TERMINAL_CWD in worker env for correct file path resolution (#41312)**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#50348: fix(kanban): pin worker TERMINAL_CWD to the task workspace**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `hermes_cli/kanban_db.py`, `tests/hermes_cli/test_kanban_worker_terminal_cwd.py`
  - fix hunks:
  - `hermes_cli/kanban_db.py`

    ```diff
    @@ -7246,6 +7246,20 @@ def _default_spawn(
             env["HERMES_TENANT"] = task.tenant
         env["HERMES_KANBAN_TASK"] = task.id
         env["HERMES_KANBAN_WORKSPACE"] = workspace
    +    # Pin TERMINAL_CWD to the task's workspace so the worker's file tools and
    +    # context-file loader anchor on the workspace, not whatever cwd the
    +    # dispatching gateway happened to export. The worker subprocess is already
    +    # launched with cwd=workspace, but TERMINAL_CWD takes precedence over the
    +    # process cwd in both file_tools._resolve_base_dir (#41312 — relative
    +    # write_file paths were landing in the gateway user's home) and
    +    # build_context_files_prompt (#34619 — workers loaded the dispatching
    +    # gateway's AGENTS.md instead of the task's). Setting it to the workspace
    +    # fixes both: the workspace is where the task's work actually happens.
    +    # Only pin a real, absolute directory — file_tools rejects relative /
    +    # sentinel TERMINAL_CWD values, so a non-dir workspace must NOT be set
    +    # here (leave the inherited value rather than write a meaningless one).
    +    if workspace and os.path.isabs(workspace) and os.path.isdir(workspace):
    +        env["TERMINAL_CWD"] = workspace
         if task.branch_name:
             env["HERMES_KANBAN_BRANCH"] = task.branch_name
         if task.current_run_id is not None:
    ```
  - `tests/hermes_cli/test_kanban_worker_terminal_cwd.py`

    ```diff
    @@ -0,0 +1,101 @@
    +"""Tests: kanban worker spawn pins TERMINAL_CWD to the task workspace.
    +
    +Regression coverage for #34619 and #41312 (same root cause): ``_default_spawn``
    +launched the worker subprocess with ``cwd=workspace`` and set
    +``HERMES_KANBAN_WORKSPACE``, but did NOT set ``TERMINAL_CWD``. Because
    +``TERMINAL_CWD`` takes precedence over the process cwd in both
    +``tools/file_tools.py::_resolve_base_dir`` (relative ``write_file`` paths) and
    +``agent_init``'s context-file loader (``AGENTS.md`` discovery), workers inherited
    +the dispatching gateway's cwd — relative writes landed in the gateway user's
    +home (#41312) and the wrong profile's ``AGENTS.md`` was loaded (#34619).
    +Pinning ``TERMINAL_CWD`` to the workspace fixes both.
    +"""
    +
    +from __future__ import annotations
    +
    +import subprocess
    +
    +
    +def _make_task(kb, *, assignee: str = "w"):
    +    return kb.Task(
    +        id="t_cwd",
    +        title="cwd pin",
    +        body=None,
    +        assignee=assignee,
    +        status="running",
    +        priority=0,
    +        created_by="test",
    +        created_at=1,
    +        started_at=None,
    +        completed_at=None,
    +        workspace_kind="dir",
    +        workspace_path=None,
    +        claim_lock="lock",
    +        claim_expires=None,
    +        tenant=None,
    +        current_run_id=1,
    +    )
    +
    +
    +def _capture_spawn_env(kb, monkeypatch, workspace: str) -> dict:
    +    monkeypatch.setattr(kb, "_resolve_hermes_argv", lambda: ["hermes"])
    +
    +    captured: dict = {}
    +
    +    class FakeProc:
    +        pid = 4242
    +
    +    def fake_popen(cmd, *args, **kwargs):
    +        captured["cmd"] = list(cmd)
    +        captured["env"] = dict(kwargs.get("env") or {})
    +        captured["cwd"] = kwargs.get("cwd")
    +        return FakeProc()
    +
    +    monkeypatch.setattr(subprocess, "Popen", fake_popen)
    +    kb._default_spawn(_make_task(kb), workspace)
    +    return captured
    +
    +
    +def test_terminal_cwd_pinned_to_workspace(monkeypatch, tmp_path):
    +    """A real, absolute workspace dir is pinned as TERMINAL_CWD."""
    +    root = tmp_path / ".hermes"
    +    (root / "profiles" / "w").mkdir(parents=True)
    +    (root / "profiles" / "w" / "config.yaml").write_text("toolsets:\n  - kanban\n", encoding="utf-8")
    +    root.joinpath("config.yaml").write_text("toolsets:\n  - kanban\n", encoding="utf-8")
    +    monkeypatch.setenv("HERMES_HOME", str(root))
    +
    +    from hermes_cli import kanban_db as kb
    +
    +    workspace = tmp_path / "ws"
    +    workspace.mkdir()
    +
    +    captured = _capture_spawn_env(kb, monkeypatch, str(workspace))
    +
    +    assert captured["env"]["TERMINAL_CWD"] == str(workspace)
    +    # The subprocess cwd and TERMINAL_CWD must agree — both anchor the workspace.
    +    assert captured["cwd"] == str(workspace)
    +    assert captured["env"]["HERMES_KANBAN_WORKSPACE"] == str(workspace)
    +
    +
    +def test_terminal_cwd_not_pinned_for_nonexistent_workspace(monkeypatch, tmp_path):
    +    """A non-directory workspace must NOT clobber the inherited TERMINAL_CWD.
    +
    +    file_tools rejects relative / sentinel TERMINAL_CWD values, so writing a
    +    meaningless (nonexistent) path would be worse than leaving the inherited
    +    one. The guard requires an existing absolute dir.
    +    """
    +    root = tmp_path / ".hermes"
    +    (root / "profiles" / "w").mkdir(parents=True)
    +    (root / "profiles" / "w" / "config.yaml").write_text("toolsets:\n  - kanban\n", encoding="utf-8")
    +    root.joinpath("config.yaml").write_text("toolsets:\n  - kanban\n", encoding="utf-8")
    +    monkeypatch.setenv("HERMES_HOME", str(root))
    +    monkeypatch.setenv("TERMINAL_CWD", "/pre/existing/anchor")
    +
    +    from hermes_cli import kanban_db as kb
    +
    +    missing = tmp_path / "does-not-exist"
    +
    +    captured = _capture_spawn_env(kb, monkeypatch, str(missing))
    +
    +    # Inherited value is preserved (not overwritten with a bogus path).
    +    assert captured["env"]["TERMINAL_CWD"] == "/pre/existing/anchor"
    ```
- **#41436: fix(kanban): pin TERMINAL_CWD to workspace so relative file paths resolve correctly**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `9630ec6c1`, `#50348`, `#41312`

## Fix status

- Merged to main in PR #50348 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-21`.
- Subsystem: `environments`
