{"repo":"NousResearch/hermes-agent","public_id":"20074","display_label":"Kanban board env variable not inherited by subprocesses","summary":"A bug where HERMES_KANBAN_BOARD is not correctly pinned or propagated to subprocess environments, causing board mis-routing during agent/chat sessions. Multiple fix attempts address environment variable inheritance at CLI and terminal levels.","subsystem":"environments","counts":{"members":5,"open_issues":0,"open_issues_distinct":0,"open_prs":1,"closed_issues":1,"merged_prs":1,"attempt_prs":3},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":20186,"merged_at":"2026-05-05T11:37:48+00:00"},"resolution":{"state":"resolved","by":"pr-20186"},"freshness":{"state":"verified","last_verified_at":"2026-05-05T11:37:48+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.4},"fix_edges":[{"issue_number":20074,"pr_number":20186,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-05-05T11:37:48+00:00"}],"members":[{"kind":"issue","number":20074,"title":"[Bug]: harness kanban CLI invoked from agent session ignores active-board pin, races current file with concurrent boards switch","url":null,"state":"closed"},{"kind":"pr","number":20094,"title":"fix(cli): pin HERMES_KANBAN_BOARD at chat boot to stop subprocess board drift","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#20186","number":20186}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":20186,"title":"fix(cli): pin HERMES_KANBAN_BOARD at chat boot to stop subprocess board drift (salvages #20094)","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["hermes_cli/main.py","tests/hermes_cli/test_pin_kanban_board_env.py"],"fix_hunks":[{"file":"hermes_cli/main.py","hunks":["@@ -1216,6 +1216,26 @@ def _launch_tui(\n     sys.exit(code)\n \n \n+def _pin_kanban_board_env() -> None:\n+    \"\"\"Pin the active kanban board into ``HERMES_KANBAN_BOARD`` for the chat session.\n+\n+    Without this, in-process tools (``kanban_*``) and shelled-out CLI calls\n+    (``hermes kanban …``) resolve the board on different paths: the env-pin if\n+    set, otherwise the global ``<root>/kanban/current`` file. A concurrent\n+    ``hermes kanban boards switch`` from another session can flip the file\n+    mid-turn, so the same chat sees its tool calls hit board A while its shell\n+    calls hit board B (#20074). Pinning at chat boot mirrors what the\n+    dispatcher already does for spawned workers.\n+    \"\"\"\n+    if os.environ.get(\"HERMES_KANBAN_BOARD\"):\n+        return\n+    try:\n+        from hermes_cli.kanban_db import get_current_board\n+        os.environ[\"HERMES_KANBAN_BOARD\"] = get_current_board()\n+    except Exception:\n+        pass\n+\n+\n def cmd_chat(args):\n     \"\"\"Run interactive chat CLI.\"\"\"\n     use_tui = getattr(args, \"tui\", False) or os.environ.get(\"HERMES_TUI\") == \"1\"","@@ -1324,6 +1344,8 @@ def cmd_chat(args):\n     if getattr(args, \"source\", None):\n         os.environ[\"HERMES_SESSION_SOURCE\"] = args.source\n \n+    _pin_kanban_board_env()\n+\n     if use_tui:\n         _launch_tui(\n             getattr(args, \"resume\", None),"]},{"file":"tests/hermes_cli/test_pin_kanban_board_env.py","hunks":["@@ -0,0 +1,75 @@\n+\"\"\"Tests for `_pin_kanban_board_env` helper invoked by `cmd_chat`.\n+\n+Regression coverage for #20074: a chat session must export the active kanban\n+board into `HERMES_KANBAN_BOARD` at boot so subprocess shell-outs (e.g.\n+`hermes kanban …`) inherit the same board the in-process kanban tools resolve.\n+Without this, a concurrent `hermes kanban boards switch` from another session\n+can flip the global current-board file mid-turn and silently divert the\n+shell calls to a different DB.\n+\"\"\"\n+import importlib\n+import os\n+\n+import pytest\n+\n+\n[redacted:email](autouse=True)\n+def _isolate_kanban_board_env():\n+    \"\"\"Snapshot `HERMES_KANBAN_BOARD` and restore it after the test.\n+\n+    `_pin_kanban_board_env()` writes to ``os.environ`` directly, bypassing\n+    any ``monkeypatch.setenv`` tracking. Without this fixture the mutation\n+    leaks into subsequent tests and breaks anything that resolves a kanban\n+    path from the env (e.g. ``TestSharedBoardPaths`` in test_kanban_db.py).\n+    \"\"\"\n+    prev = os.environ.get(\"HERMES_KANBAN_BOARD\")\n+    os.environ.pop(\"HERMES_KANBAN_BOARD\", None)\n+    try:\n+        yield\n+    finally:\n+        if prev is None:\n+            os.environ.pop(\"HERMES_KANBAN_BOARD\", None)\n+        else:\n+            os.environ[\"HERMES_KANBAN_BOARD\"] = prev\n+\n+\n+def test_pin_writes_resolved_board_when_env_unset(monkeypatch):\n+    main_mod = importlib.import_module(\"hermes_cli.main\")\n+\n+    import hermes_cli.kanban_db as kdb\n+    monkeypatch.setattr(kdb, \"get_current_board\", lambda: \"space\")\n+\n+    main_mod._pin_kanban_board_env()\n+\n+    assert main_mod.os.environ.get(\"HERMES_KANBAN_BOARD\") == \"space\"\n+\n+\n+def test_pin_does_not_overwrite_existing_env(monkeypatch):\n+    monkeypatch.setenv(\"HERMES_KANBAN_BOARD\", \"preset\")\n+    main_mod = importlib.import_module(\"hermes_cli.main\")\n+\n+    import hermes_cli.kanban_db as kdb\n+\n+    def _explode():\n+        raise AssertionError(\"get_current_board must not be called when env is set\")\n+\n+    monkeypatch.setattr(kdb, \"get_current_board\", _explode)\n+\n+    main_mod._pin_kanban_board_env()\n+\n+    assert main_mod.os.environ.get(\"HERMES_KANBAN_BOARD\") == \"preset\"\n+\n+\n+def test_pin_swallows_resolution_failures(monkeypatch):\n+    main_mod = importlib.import_module(\"hermes_cli.main\")\n+\n+    import hermes_cli.kanban_db as kdb\n+\n+    def _boom():\n+        raise RuntimeError(\"disk gone\")\n+\n+    monkeypatch.setattr(kdb, \"get_current_board\", _boom)\n+\n+    main_mod._pin_kanban_board_env()\n+\n+    assert \"HERMES_KANBAN_BOARD\" not in main_mod.os.environ\n"]}]},{"kind":"pr","number":36742,"title":"fix(terminal): propagate HERMES_KANBAN_BOARD to subprocess env to prevent board mis-routing","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"b22b3f506a34cf848d68bb2ab17b68b0bc8ec152","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":142272,"title":"fix(skills): kanban env gate misreads chat-boot BOARD pin as worker identity","url":null,"state":"open","close_reason":{"category":"silent_close","implication":"active"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}