Kanban board env variable not inherited by subprocesses
Cluster 20074 · environments. Data as of
1 open item. Fix merged in #20186 on 5 May 2026. Check it covers your case.
The short answer
- Known?
- Yes. 5 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #20186. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
resolved, deciding PR #20186. That is separate from the fix event. - Still open
- 0 issues and 1 PR.
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.
Open issues0
Open PRs1
- PR#142272fix(skills): kanban env gate misreads chat-boot BOARD pin as worker identity
Fixes on record
-
#20186 fix(cli): pin HERMES_KANBAN_BOARD at chat boot to stop subprocess board drift (salvages #20094)
files:
hermes_cli/main.py,tests/hermes_cli/test_pin_kanban_board_env.pyShow the diff
-
hermes_cli/main.py@@ -1216,6 +1216,26 @@ def _launch_tui( sys.exit(code) +def _pin_kanban_board_env() -> None: + """Pin the active kanban board into ``HERMES_KANBAN_BOARD`` for the chat session. + + Without this, in-process tools (``kanban_*``) and shelled-out CLI calls + (``hermes kanban …``) resolve the board on different paths: the env-pin if + set, otherwise the global ``<root>/kanban/current`` file. A concurrent + ``hermes kanban boards switch`` from another session can flip the file + mid-turn, so the same chat sees its tool calls hit board A while its shell + calls hit board B (#20074). Pinning at chat boot mirrors what the + dispatcher already does for spawned workers. + """ + if os.environ.get("HERMES_KANBAN_BOARD"): + return + try: + from hermes_cli.kanban_db import get_current_board + os.environ["HERMES_KANBAN_BOARD"] = get_current_board() + except Exception: + pass + + def cmd_chat(args): """Run interactive chat CLI.""" use_tui = getattr(args, "tui", False) or os.environ.get("HERMES_TUI") == "1" @@ -1324,6 +1344,8 @@ def cmd_chat(args): if getattr(args, "source", None): os.environ["HERMES_SESSION_SOURCE"] = args.source + _pin_kanban_board_env() + if use_tui: _launch_tui( getattr(args, "resume", None), -
tests/hermes_cli/test_pin_kanban_board_env.py@@ -0,0 +1,75 @@ +"""Tests for `_pin_kanban_board_env` helper invoked by `cmd_chat`. + +Regression coverage for #20074: a chat session must export the active kanban +board into `HERMES_KANBAN_BOARD` at boot so subprocess shell-outs (e.g. +`hermes kanban …`) inherit the same board the in-process kanban tools resolve. +Without this, a concurrent `hermes kanban boards switch` from another session +can flip the global current-board file mid-turn and silently divert the +shell calls to a different DB. +""" +import importlib +import os + +import pytest + + [redacted:email](autouse=True) +def _isolate_kanban_board_env(): + """Snapshot `HERMES_KANBAN_BOARD` and restore it after the test. + + `_pin_kanban_board_env()` writes to ``os.environ`` directly, bypassing + any ``monkeypatch.setenv`` tracking. Without this fixture the mutation + leaks into subsequent tests and breaks anything that resolves a kanban + path from the env (e.g. ``TestSharedBoardPaths`` in test_kanban_db.py). + """ + prev = os.environ.get("HERMES_KANBAN_BOARD") + os.environ.pop("HERMES_KANBAN_BOARD", None) + try: + yield + finally: + if prev is None: + os.environ.pop("HERMES_KANBAN_BOARD", None) + else: + os.environ["HERMES_KANBAN_BOARD"] = prev + + +def test_pin_writes_resolved_board_when_env_unset(monkeypatch): + main_mod = importlib.import_module("hermes_cli.main") + + import hermes_cli.kanban_db as kdb + monkeypatch.setattr(kdb, "get_current_board", lambda: "space") + + main_mod._pin_kanban_board_env() + + assert main_mod.os.environ.get("HERMES_KANBAN_BOARD") == "space" + + +def test_pin_does_not_overwrite_existing_env(monkeypatch): + monkeypatch.setenv("HERMES_KANBAN_BOARD", "preset") + main_mod = importlib.import_module("hermes_cli.main") + + import hermes_cli.kanban_db as kdb + + def _explode(): + raise AssertionError("get_current_board must not be called when env is set") + + monkeypatch.setattr(kdb, "get_current_board", _explode) + + main_mod._pin_kanban_board_env() + + assert main_mod.os.environ.get("HERMES_KANBAN_BOARD") == "preset" + + +def test_pin_swallows_resolution_failures(monkeypatch): + main_mod = importlib.import_module("hermes_cli.main") + + import hermes_cli.kanban_db as kdb + + def _boom(): + raise RuntimeError("disk gone") + + monkeypatch.setattr(kdb, "get_current_board", _boom) + + main_mod._pin_kanban_board_env() + + assert "HERMES_KANBAN_BOARD" not in main_mod.os.environ
-
2 PRs closed without merging
From the close comments: done elsewhere 1 · merged via another change 1
Closed issues · 1
- Closed#20074[Bug]: harness kanban CLI invoked from agent session ignores active-board pin, races current file with concurrent boards switch
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.40: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Subsystem
- environments
- Merged PRs
- #20186
- Closed issues
- 1
- Attempts
- 2 PRs closed without merging
- Fix link checked
- 5 May 2026